Faster second-best MST algorithm?

前端 未结 2 1987
夕颜
夕颜 2021-02-15 06:38

I am struggling with this.

We can get MST using Kruskal\'s algorithm or Prim\'s algorithm for the MST.

And for \"second-best\" MST, I can:

  1. first g
2条回答
  •  独厮守ぢ
    2021-02-15 07:16

    I'll describe polylogarithmic solution to the problem. Let's introduce some definitions. We'll denote:

    1. Set of graph's vertices by V, set of graph's edges by E and set of MST edges by T.
    2. Edge of graph between vertices v and u by {v, u}.
    3. Weight of edge e by W(e) and weight of MST by W(T).

    Let's consider function MaxEdge(v, u), which is equal to the edge with the largest weight on the simple path between v and u that belongs to T. If there are several edges with maximum weight MaxEdge(v, u) can be equal to any of them.

    To find the second best MST, we need to find such edge x = {p, q}, that:

    1. x does not belong to T.
    2. Function W(x) - W(MaxEdge(p, q)) is minimal possible.

    It's possible to prove that the second best MST can be constructed by removing MaxEdge(p, q) from T and then adding x = {p, q} to T.

    Now let's build a data structure that will be able to compute MaxEdge(p, q) in O(log|V|).

    Let's pick a root for the tree T (it can be any vertex). We'll call the number of edges in the simple path between vertex v and the root - the depth of vertex v, and denote it Depth(v). We can compute Depth(v) for all vertices in O(|V|) by depth first search.

    Let's compute two functions, that will help us to calculate MaxEdge(p, q):

    1. Parent(v, i), which is equal to the vertex that is a parent (might be non direct parent) of vertex v with depth equal to Depth(v) - 2^i.
    2. MaxParentEdge(v, i), which is equal to MaxEdge(v, Parent(v, i)).

    Both of them can be computed by a recurrence function with memorisation in O(|V|log|V|).

    // Assumes that 2^i <= Depth(v)
    Vertex Parent(Vertex v, Vertex i) {
        if (i == 0) return direct_parent[v];
        if (Memorized(v, i)) return memorized_parent[v][i]; 
        memorized_parent[v][i] = Parent(Parent(v, i - 1), i - 1);
        return memorized_parent[v][i];
    }
    
    Edge MaxParentEdge(Vertex v, Vertex i) {
        if (i == 0) return Edge(v, direct_parent[v]);
        if (Memorized(v, i)) return memorized_parent_edge[v][i]; 
        Edge e1 = MaxParentEdge(v, i - 1);
        Edge e2 = MaxParentEdge(Parent(v, i - 1), i - 1);
        if (W(e1) > W(e2)) {
            memorized_parent_edge[v][i] = e1;
        } else {
            memorized_parent_edge[v][i] = e2;
        }
        return memorized_parent_edge[v][i];
    }
    

    Before we are ready to compute MaxEdge(p, q), let's introduce the final definition. Lca(v, u) will denote lowest common ancestor of vertices v and u in the rooted tree T. There are a lot of well known data structures that allows to compute Lca(v, u) query in O(log|V|) or even in O(1) (you can find the list of articles at Wikipedia).

    To compute MaxEdge(p, q) we will divide the path between p and q into two parts: from p to Lca(p, q), from Lca(p, q) to q. Each of these parts looks like a path from a vertex to some of its parents, therefore we can use our Parent(v, i) and MaxParentEdge(v, i) functions to compute MaxEdge for these parts.

    Edge MaxEdge(Vertex p, Vertex q) {
        Vertex mid = Lca(p, q);
        if (p == mid || q == mid) {
           if (q == mid) return QuickMaxEdge(p, mid);
           return QuickMaxEdge(q, mid);
        }
        // p != mid and q != mid
        Edge e1 = QuickMaxEdge(p, mid);
        Edge e2 = QuickMaxEdge(q, mid);
        if (W(e1) > W(e2)) return e1;
        return e2;
    }
    
    Edge QuickMaxEdge(Vertex v, Vertex parent_v) {
        Edge maxe = direct_parent[v];
        string binary = BinaryRepresentation(Depth(v) - Depth(parent_v));
        for (int i = 0; i < binary.size(); ++i) {
            if (binary[i] == '1') {
                Edge e = MaxParentEdge(v, i);
                if (W(e) > W(maxe)) maxe = e;
                v = Parent(v, i);
            }
        }
        return maxe;
    }
    

    Basically function QuickMaxEdge(v, parent_v) does jumps of length 2^i to cover distance between parent_v and v. During a jump it uses precomputed values of MaxParentEdge(v, i) to update the answer.

    Considering that MaxParentEdge(v, i) and Parent(v, i) is precomputed, MaxEdge(p, q) works in O(log|V|), which leads us to an O(|E|log|V|) solution to the initial problem. We just need to iterate over all edges that does not belong T and compute W(e) - MaxEdge(p, q) for each of them.

提交回复
热议问题