Lonely Island#9044

Lonely Island (Editorial)

By the problem statement, once you leave an island you can never return to it. Hence the graph whose vertices are the islands and whose edges are the bridges is a directed acyclic graph. You can get stuck only on an island with no outgoing bridges, that is, in a dead-end vertex with zero out-degree: OutDegree[v] = 0.

Let prob[v] denote the probability that during the walk we reach vertex . For the starting vertex prob[r] = 1; for all other vertices initially prob[v] = 0. Since the graph contains no cycles, each vertex can be visited at most once.

Suppose OutDegree[u] edges leave vertex . Being at , we choose one of them uniformly at random, so we move along each edge u \to v with probability prob[u] / OutDegree[u]. Vertex can be reached only from its direct predecessors. Therefore, prob[v] equals the sum of the contributions of all edges u \to v entering :

prob[v] = \sum_{u \to v} prob[u] / OutDegree[u]

The value prob[v] can be computed only when the probabilities of all its predecessors are final. This is exactly the processing order given by a topological sort: every vertex appears after all vertices from which edges lead into it. Therefore, we build the topological order using Kahn's algorithm and at the same time "push" the probabilities along the edges.

The algorithm is as follows:

Initially, the queue contains all vertices with zero in-degree, not only the starting one. Vertices unreachable from are processed as well, but their probability remains zero and does not affect the answer.

Finding the answer. Among the dead-end vertices, find the maximum probability . Then print, in increasing order, the indices of all dead-end vertices such that |prob[v] - mx| \le 10^{-9}. Comparison with a tolerance is necessary because the probabilities are computed in floating-point numbers with rounding errors.

Complexity. Each vertex is pushed into and extracted from the queue exactly once, and each edge is examined once. The running time of the algorithm is O(n + m).

Example

Let us simulate the algorithm on the graph from the example. Next to each vertex, the probability prob[v] of reaching it is shown. Processed vertices are shaded gray, the edges along which the probability is distributed at the current step are highlighted in blue, and already examined edges are drawn dashed.

Initially (left figure) prob[1] = 1, and the probabilities of all other vertices are . Vertex is the only vertex with zero in-degree, so it is the first to be extracted from the queue.

Four edges leave vertex , so the probability prob[1] = 1 is split equally among vertices and : each of them receives 1 / 4 = 0.25 (right figure). After removing the edges leaving vertex , the in-degrees of vertices and become zero, and they are pushed into the queue.

Next, vertex is processed. Two edges leave it, so prob[2] / 2 = 0.25 / 2 = 0.125 is added to and . We get prob[4] = prob[5] = 0.375 (bottom left figure). The in-degree of vertex becomes zero, and it is pushed into the queue.

Vertex is processed next. A single edge 3 \to 4 leaves it, so increases by prob[3] / 1 = 0.25 and becomes (bottom right figure). Now vertex is pushed into the queue as well.

Vertices and have no outgoing edges, so processing them does not change any probabilities. The resulting topological order is 1, 2, 3, 5, 4.

The dead-end vertices are and , with probabilities and respectively. Their sum equals : we are certain to get stuck somewhere. The maximum probability is reached at the single vertex , which is the answer.

Algorithm implementation

Declare the constant — the tolerance for comparing floating-point numbers.

#define EPS 1e-9

Store the input graph as an adjacency list . Store the in-degrees and out-degrees of the vertices in the arrays InDegree and OutDegree. The queue is used in Kahn's algorithm. In prob[v] we compute the probability of reaching vertex .

vector<vector<int>> g;
vector<int> InDegree, OutDegree;
deque<int> q;
vector<double> prob;

Read the number of islands , the number of bridges and the index of the starting island . Initialize the adjacency list and the degree arrays.

scanf("%d %d %d", &n, &m, &r);
g.assign(n + 1, vector<int>());
InDegree.assign(n + 1, 0);
OutDegree.assign(n + 1, 0);

Read the bridges and build the graph. For each edge a \to b, increase the out-degree of vertex and the in-degree of vertex .

for (i = 0; i < m; i++)
{
  scanf("%d %d", &a, &b);
  g[a].push_back(b);
  OutDegree[a]++;
  InDegree[b]++;
}

Push all vertices with zero in-degree into the queue — the topological sort starts with them.

for (i = 1; i < InDegree.size(); i++)
  if (InDegree[i] == 0) q.push_back(i);

The probability of being at the starting vertex is . For all other vertices the initial probability is : the function fills the array with zeros.

prob.resize(n + 1);
prob[r] = 1;

Run Kahn's algorithm. Vertices are extracted from the queue in topological order.

while (!q.empty())
{

Extract a vertex from the queue. By this moment all its predecessors have already been processed, so the value prob[v] is final. Iterate over the edges (v, to). OutDegree[v] edges leave vertex , and each of them is chosen with equal probability. Therefore, we move along the edge v \to to with probability prob[v] / OutDegree[v]. Add this value to prob[to].

  v = q.front(); q.pop_front();
  for (int to : g[v])
  {
    prob[to] += prob[v] / OutDegree[v];

Remove the edge (v, to) from the graph — decrease the in-degree of vertex .

    InDegree[to]--;

If the in-degree of vertex becomes zero, all its predecessors have been processed and the value prob[to] is final. Push into the queue.

    if (InDegree[to] == 0) q.push_back(to);
  }
}

Find the maximum probability among the dead-end vertices — the islands with no outgoing bridges (OutDegree[i] = 0).

mx = 0;
for (i = 1; i <= n; i++)
  if (OutDegree[i] == 0 && prob[i] > mx) mx = max(mx, prob[i]);

Print, in increasing order, the indices of all dead-end vertices whose probability differs from by at most . There may be several such vertices.

for (i = 1; i <= n; i++)
  if (OutDegree[i] == 0 && fabs(prob[i] - mx) <= EPS)
    printf("%d ", i);