Catch the Plane#11554

Catch the Plane (Editorial)

To solve the problem, we use a dynamic programming approach where routes are considered in decreasing order of departure time.

Let's consider the following state:

What is the maximum probability of reaching the airport on time from under an optimal strategy? We denote this quantity as best(x, time). The parameter can take values up to 10^{18}, so the number of states is effectively infinite. If we are standing at a station waiting, the situation only changes at moments when a bus departs. Therefore, we'll consider only bus departure times.

When we compute best(B, S) for a given route, transitions are possible only to states with later times:

In both cases, the next moment in time is strictly later than the current one. Thus, if we process the routes in decreasing order of departure time, all the necessary future values will already have been computed.

Dynamic Programming Transitions

Consider the following route:

Then there are two possible actions:

. We try to take the bus. If the bus runs, the probability of this event is . In this case, we move to the state (E, T) and then continue acting optimally:

p \cdot best(E, next~route > T)

If you arrive exactly at the departure time, there is no time left to board the bus. Therefore, we look for the next departure from station that is strictly greater than .

If the bus is canceled, the probability of this event is 1 - p. In this case, we remain at station and wait for the next route:

(1 - p) \cdot best(B, next~route > S)

If several buses depart from the station at the same time, you can attempt to board only one of them. If that bus is canceled, you cannot board another bus from station at time . You must wait for a route whose departure time is strictly greater than .

. We do not try to take the bus at all. In that case, we move to the next bus at the same station:

best(B, next~route \ge S)

Thus, the transition has the following form:

best(B, S) = max(p \cdot best(E, next~route > T) + (1 - p) \cdot best(B, next~route > S), \\ best(B, next~route ≥ S))

Example

Sort the routes in the first example by station number and departure time. On the right, list the pairs (departure time, route number) sorted by departure time.

Route number

Departure station

Arrival station

Departure time

Arrival time

Probability

Pair

0

0

1

0

900

0.2

(0, 0)

1

0

2

100

500

1.0

(100, 1)

2

0

3

200

400

0.5

(200, 2)

3

0

1

700

900

0.1

(500, 5)

4

1

1

1001

1001

1.0

(500, 7)

5

2

1

500

700

1.0

(501, 6)

6

2

1

501

701

0.1

(550, 8)

7

3

1

500

800

0.1

(700, 3)

8

3

0

550

650

0.9

(1001, 4)

Compute the probabilities of arriving at the airport in decreasing order of bus route departure times. The notation time^+ denotes a time strictly greater than .

best(1, 1001) = 1.0; \\
best(0, 700) = 0.1 \cdot best(1, 900^+) = 0.1 \cdot 1 = 0.1; \\
best(3, 550) = 0.9 \cdot best(0, 650^+) + 0.1 \cdot best(3, 550^+) = 0.9 \cdot 0.1 = 0.09; \\
best(2, 501) = 0.1 \cdot best(1, 701^+) + 0.9 \cdot best(2, 501^+) = 0.1 \cdot 1.0 + 0.9 \cdot 0 = 0.1; \\
best(3, 500) = 0.1 \cdot best(1, 800^+) + 0.9 \cdot best(3, 500^+) = 0.1 \cdot 1.0 + 0.9 \cdot 0.09 = 0.181; \\ best(3, 500) = max(best(3, 500), best(3, 550)) = max(0.181, 0.09) = 0.181; \\
best(2, 500) = 1.0 \cdot best(1, 700^+) + 0.0 \cdot best(2, 500^+) = 1.0; \\ best(2, 500) = max(best(2, 500), best(2, 501)) = max(1.0, 0.1) = 1.0; \\
best(0, 200) = 0.5 \cdot best(3, 400^+) + 0.5 \cdot best(0, 200^+) = 0.5 \cdot 0.181 + 0.5 \cdot 0.1 = 0.0905 + 0.05 = 0.1405; \\ best(0, 200) = max(best(0, 200), best(0, 700)) = max(0.1405, 0.1) = 0.1405; \\
best(0, 100) = 1.0 \cdot best(2, 500^+) + 0.0 \cdot best(0, 100^+) = 1.0 \cdot 0.1 = 0.1; \\ best(0, 100) = max(best(0, 100), best(0, 200)) = max(0.1405, 0.1) = 0.1405; \\
best(0, 0) = 0.2 \cdot best(1, 900^+) + 0.8 \cdot best(0, 0^+) = 0.2 \cdot 1.0 + 0.8 \cdot 0.1405 = 0.2 + 0.1124 = 0.3124; \\ best(0, 0) = max(best(0, 0), best(0, 100)) = max(0.3124, 0.1405) = 0.3124; \\

Algorithm implementation

Define the structure of a bus route, . It contains:

struct Route
{
  int B, E;
  long long S, T;
  double P, value;

Bus routes are sorted:

  bool operator<(const Route& r) const
  {
    return S < r.S;
  }
};

The main part of the program. Read the input data.

scanf("%d %d %lld", &m, &n, &k);

Create a list of all routes grouped by departure station. The vector has size , which is the number of stations. Thus,

vector<vector<Route>> v(n);
for (int i = 0; i < m; i++)
{
  scanf("%d %d %lld %lld %lf", &r.B, &r.E, &r.S, &r.T, &r.P);

The maximum probability of reaching the airport from this route is initially unknown, so we initialize it to .

  r.value = 0.0;

Add route to the list of routes that start from .

  v[r.B].push_back(r);
}

Now we create a dummy route that represents the moment when the passenger is already at the airport (station ). Both the starting and ending stations are the airport.

r.B = 1;
r.E = 1;

The probability that this route succeeds is .

r.value = 1.0;
r.P = 1.0;

The departure time and arrival time are later than all others.

r.S = k + 1;
r.T = k + 1;

Add this dummy route to the list of routes departing from the airport.

v[1].push_back(r);

For each station , sort all routes departing from that station by departure time . This is necessary to efficiently find the next route later using upper\_bound.

for (int i = 0; i < n; i++)
  sort(v[i].begin(), v[i].end());

Create a single processing order for all routes based on departure time so that the dynamic programming can proceed from later routes to earlier ones.

Declare a vector of pairs, , which stores for each route:

. Departure time .

. Route indices:

Thus, each entry in indicates: "Route v[i][j] departs at time ".

vector<pair<long long, pair<int, int>>> ord;
for (int i = 0; i < v.size(); i++)
  for (int j = 0; j < v[i].size(); j++)
    ord.push_back({ v[i][j].S, {i, j} });

Sort the vector in ascending order of departure times for all routes, regardless of the station.

sort(ord.begin(), ord.end());

The variable computes the maximum probability of catching the flight when starting from station at time .

double ret = 0.0;

Iterate through the routes in decreasing order of departure time, so that for the current route, the value of all future routes (those departing later) has already been computed, allowing us to correctly compute the optimal probability.

for (int itOrd = m; itOrd >= 0; itOrd--)
{

Let us store the departure time of the current route in the variable startTime.

  long long startTime = ord[itOrd].first;

is a pair of indices that allows us to locate the current route in the array of routes : the route itself is stored in v[p.first][p.second].

  auto p = ord[itOrd].second;

If the departure time startTime > k (meaning it is impossible to catch the plane), we simply skip this route.

  if (startTime > k) continue;

is the index of the departure station for the current route.

  int st = p.first;

is the current route for which we compute the .

  Route& r = v[p.first][p.second];

Initialize the for the current route to zero.

  r.value = 0.0;

is a temporary object of type that is used to find the next route using upper\_bound. We do not use all its fields — only and , which are needed for comparison during the search.

  Route query;

Configure for the following scenario: we want to find the first bus from the same station that departs strictly later than the current time , if the current bus is canceled (does not run).

  query.B = r.B;
  query.S = r.S;

We look for the first bus from the same station that departs later.

  auto it = upper_bound(v[st].begin() + p.second, v[st].end(), query);
  if (it != v[st].end())

If such a route exists, increase r.value by the probability of success through it.

    r.value += (1.0 - r.P) * it->value;

Consider the scenario where the bus actually runs. In this case, at time we arrive at station . We prepare the object to search for the next route. We formulate the query: "Find a bus that departs from station later than time ".

  query.B = r.E;
  query.S = r.T;

The variable stores the index of station , where we have arrived.

  int d = r.E;

Search for the next bus after arriving at in the list of routes using binary search. Its departure time from must be strictly greater than , because according to the problem statement, if you arrive exactly at the departure time, you cannot board the bus.

  it = upper_bound(v[d].begin(), v[d].end(), query);
  if (it != v[d].end())

If such a route is found, it means we can transfer to it. Add the contribution of the probability p \cdot best(E, next~route > T).

    r.value += r.P * it->value;

The next option we consider is to skip the current bus. That is, we do not even consider the option of boarding it or not. We wait for the next bus at the same station.

Check if the next bus exists at this station. Recall that the current route is stored in v[p.first][p.second].

  if (p.second + 1 < v[p.first].size())

If the bus exists and the probability of successfully reaching the airport using it is higher, we update . Here:

    r.value = max(r.value, v[p.first][p.second + 1].value);

Update the overall answer. We select the best option only among the buses that depart from the initial (zero) station

  if (r.B == 0) ret = max(ret, r.value);
}

Print the answer.

printf("%.6lf\n", ret);