Baku Metro#11865

Baku Metro (Editorial)

The metro map is a connected undirected graph with vertices and edges. A connected graph on vertices with exactly n - 1 edges is a tree. Adding one more edge to a tree creates exactly one cycle. Therefore, the graph in the problem consists of a single cycle (the ring line), with trees (possibly empty) attached to its vertices.

For each vertex, we need to find the length of the shortest path to the nearest vertex of the cycle. For vertices on the cycle, the answer is . The problem is solved in two stages.

Stage 1. Finding the cycle: repeated removal of leaves

Compute the degrees of all vertices. Push all vertices of degree , that is, the leaves, into a queue. While the queue is not empty, pop a vertex from it and mark it as removed. Then decrease by the degree of each of its neighbors that has not yet been removed. If a neighbor's degree becomes , it has turned into a leaf itself, so we push it into the queue.

When the process finishes, only the cycle vertices remain unremoved. This follows from two observations:

Stage 2. Multi-source breadth-first search

Set d[v] = 0 for all cycle vertices and d[v] = –1 (not visited) for all other vertices. Push all cycle vertices into the queue at once and run a breadth-first search from them. When a vertex is popped from the queue, each of its unvisited neighbors gets d[w] = d[u] + 1 and is pushed into the queue.

A breadth-first search from a set of sources processes vertices in non-decreasing order of their distance to that set. Therefore, equals the minimum number of moves from to the ring line. Since everything outside the cycle is a tree, the path from each station to the ring is in fact unique.

Example

In the first example, all vertices are part of a single cycle, so the distance for each of them is .

In the second example, the cycle consists of vertices , and , while the remaining vertices are located at some distance from it.

Algorithm implementation

Declare the adjacency list of the graph and the auxiliary arrays:

The queue is used in both stages of the algorithm: first for the repeated removal of leaves, then for the multi-source breadth-first search.

vector<vector<int> > g;
vector<int> used, dist, deg;
queue<int> q;

Read the input data. Build the graph.

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

Compute the initial degrees of the vertices: the degree of vertex equals the size of its adjacency list . Push all leaves, that is, vertices of degree , into the queue and immediately mark them as removed (used[i] = 1).

used.resize(n + 1, 0);
deg.resize(n + 1, 0);
for (i = 1; i <= n; i++)
{
  deg[i] = g[i].size();
  if (deg[i] == 1)
  {
    q.push(i);
    used[i] = 1;
  }
}

Remove the leaves one by one. Outwardly this loop resembles a breadth-first search, but a vertex is pushed into the queue not when it is first discovered, but only when it becomes a leaf itself. Pop a vertex from the queue and iterate over its neighbors . Neighbors already marked as removed are skipped. For each neighbor that has not been removed, decrease its degree by , since the edge (v, to) disappears from the graph together with vertex .

while (!q.empty())
{
  int v = q.front(); q.pop();
  for (int to : g[v])
    if (used[to] == 0)
    {
      deg[to]--;

If after the decrease the degree of vertex has become , then it has only one neighbor left that has not been removed, so it has turned into a leaf itself. Push it into the queue and mark it as removed. Cycle vertices never reach degree , so the process stops at the ring line.

      if (deg[to] == 1)
      {
        q.push(to);
        used[to] = 1;
      }
    }
}

At this point the equality used[i] = 0 holds if and only if vertex belongs to the cycle.

Initialize the array with the value (no vertex has been visited). The queue is already empty at this point, but assigning an empty queue explicitly resets it before the second traversal.

dist = vector<int>(n + 1, -1);
q = queue<int>();

Then push every cycle vertex into the queue and set dist[i] = 0 for it — all vertices of the ring line simultaneously become the sources of the breadth-first search.

for (i = 1; i <= n; i++)

  if (used[i] == 0)
  {
    q.push(i);
    dist[i] = 0;
  }

Run the multi-source breadth-first search. Pop a vertex from the queue. Each of its unvisited neighbors to~(dist[to] = –1) gets the distance dist[v] + 1 and is pushed into the queue. Cycle vertices are marked with distance from the very beginning, so the traversal never returns to the ring and spreads only into the attached trees. Since vertices are popped from the queue in non-decreasing order of distance, each value dist[to] is the shortest one.

while (!q.empty())
{
  int v = q.front(); q.pop();
  for (int to : g[v])
    if (dist[to] == -1)
    {
      q.push(to);
      dist[to] = dist[v] + 1;
    }
  }

Print the distances from the stations to the ring line in the order of their numbers.

for (i = 1; i <= n; i++)
   printf("%d ", dist[i]);
printf("\n");