2020.1.21にチェックインして、最小スパニングツリーでキーエッジと疑似キーエッジを見つけます

トピック:

ノードに0からn-1までの番号が付けられた、n点の重み付き無向連結グラフと、エッジの配列を提供します。ここで、edges [i] = [fromi、toi、weighti]は、fromiノードとtoiノードの間に間にある重み付き無向エッジ。最小スパニングツリー(MST)は、特定のグラフのエッジのサブセットであり、すべてのノードを接続し、ループはなく、これらのエッジの重みの合計が最小になります。
与えられたグラフで最小全域木のすべてのキーエッジと疑似キーエッジを見つけてください。エッジがグラフから削除されると、最小スパニングツリーの重みが増加するため、クリティカルエッジであると言えます。疑似クリティカルエッジは、一部の最小スパニングツリーに表示される可能性があるが、すべての最小スパニングツリーには表示されないエッジです。
キーエッジと疑似キーエッジの添え字は、それぞれ任意の順序で返すことができることに注意してください。
例1:
ここに画像の説明を挿入
入力:n = 5、エッジ= [[0,1,1]、[1,2,1]、[2,3,2]、[0,3,2]、[0,4,3 ]、[3,4,3]、[1,4,6]]
出力:[[0,1]、[2,3,4,5]]
説明:上の図は、指定された図を示しています。
ここに画像の説明を挿入
0番目のエッジと1番目のエッジはすべての最小スパニングツリーに表示されるため、これらがキーエッジであることに注意してください。これら2つの添え字を出力の最初のリストとして使用します。
エッジ2、3、4、および5は、すべてのMSTの残りのエッジであるため、疑似クリティカルエッジです。それらを出力の2番目のリストとして使用します。

例2
ここに画像の説明を挿入
入力:n = 4、エッジ= [[0,1,1]、[1,2,1]、[2,3,1]、[0,3,1]]
出力:[[]、[ 0,1,2,3]]
説明:4つのエッジの重みが同じであることがわかります。そのうちの3つを選択して、MSTを形成できます。したがって、4つのエッジはすべて疑似クリティカルエッジです。

コード:

// 并查集模板
class UnionFind {
    
    
public:
    vector<int> parent;
    vector<int> size;
    int n;
    // 当前连通分量数目
    int setCount;
    
public:
    UnionFind(int _n): n(_n), setCount(_n), parent(_n), size(_n, 1) {
    
    
        iota(parent.begin(), parent.end(), 0);
    }
    
    int findset(int x) {
    
    
        return parent[x] == x ? x : parent[x] = findset(parent[x]);
    }
    
    bool unite(int x, int y) {
    
    
        x = findset(x);
        y = findset(y);
        if (x == y) {
    
    
            return false;
        }
        if (size[x] < size[y]) {
    
    
            swap(x, y);
        }
        parent[y] = x;
        size[x] += size[y];
        --setCount;
        return true;
    }
    
    bool connected(int x, int y) {
    
    
        x = findset(x);
        y = findset(y);
        return x == y;
    }
};

// Tarjan 算法求桥边模版
class TarjanSCC {
    
    
private:
    const vector<vector<int>>& edges;
    const vector<vector<int>>& edgesId;
    vector<int> low;
    vector<int> dfn;
    vector<int> ans;
    int n;
    int ts;

private:
    void getCuttingEdge_(int u, int parentEdgeId) {
    
    
        low[u] = dfn[u] = ++ts;
        for (int i = 0; i < edges[u].size(); ++i) {
    
    
            int v = edges[u][i];
            int id = edgesId[u][i];
            if (dfn[v] == -1) {
    
    
                getCuttingEdge_(v, id);
                low[u] = min(low[u], low[v]);
                if (low[v] > dfn[u]) {
    
    
                    ans.push_back(id);
                }
            }
            else if (id != parentEdgeId) {
    
    
                low[u] = min(low[u], dfn[v]);
            }
        }
    }

public:
    TarjanSCC(int n_, const vector<vector<int>>& edges_, const vector<vector<int>>& edgesId_): \
        edges(edges_), edgesId(edgesId_), low(n_, -1), dfn(n_, -1), n(n_), ts(-1) {
    
    }
    
    vector<int> getCuttingEdge() {
    
    
        for (int i = 0; i < n; ++i) {
    
    
            if (dfn[i] == -1) {
    
    
                getCuttingEdge_(i, -1);
            }
        }
        return ans;
    }
};

class Solution {
    
    
public:
    vector<vector<int>> findCriticalAndPseudoCriticalEdges(int n, vector<vector<int>>& edges) {
    
    
        int m = edges.size();
        for (int i = 0; i < m; ++i) {
    
    
            edges[i].push_back(i);
        }
        sort(edges.begin(), edges.end(), [](const auto& u, const auto& v) {
    
    
            return u[2] < v[2];
        });

        UnionFind uf(n);
        vector<vector<int>> ans(2);
        vector<int> label(m);
        for (int i = 0; i < m;) {
    
    
            // 找出所有权值为 w 的边,下标范围为 [i, j)
            int w = edges[i][2];
            int j = i;
            while (j < m && edges[j][2] == edges[i][2]) {
    
    
                ++j;
            }

            // 存储每个连通分量在图 G 中的编号
            unordered_map<int, int> compToId;
            // 图 G 的节点数
            int gn = 0;
            
            for (int k = i; k < j; ++k) {
    
    
                int x = uf.findset(edges[k][0]);
                int y = uf.findset(edges[k][1]);
                if (x != y) {
    
    
                    if (!compToId.count(x)) {
    
    
                        compToId[x] = gn++;
                    }
                    if (!compToId.count(y)) {
    
    
                        compToId[y] = gn++;
                    }
                }
                else {
    
    
                    // 将自环边标记为 -1
                    label[edges[k][3]] = -1;
                }
            }
            
            // 图 G 的边
            vector<vector<int>> gm(gn), gmid(gn);
            
            for (int k = i; k < j; ++k) {
    
    
                int x = uf.findset(edges[k][0]);
                int y = uf.findset(edges[k][1]);
                if (x != y) {
    
    
                    int idx = compToId[x], idy = compToId[y];
                    gm[idx].push_back(idy);
                    gmid[idx].push_back(edges[k][3]);
                    gm[idy].push_back(idx);
                    gmid[idy].push_back(edges[k][3]);
                }
            }

            vector<int> bridges = TarjanSCC(gn, gm, gmid).getCuttingEdge();
            // 将桥边(关键边)标记为 1
            for (int id: bridges) {
    
    
                ans[0].push_back(id);
                label[id] = 1;
            }

            for (int k = i; k < j; ++k) {
    
    
                uf.unite(edges[k][0], edges[k][1]);
            }

            i = j;
        }

        // 未标记的边即为非桥边(伪关键边)
        for (int i = 0; i < m; ++i) {
    
    
            if (!label[i]) {
    
    
                ans[1].push_back(i);
            }
        }

        return ans;
    }
};

おすすめ

転載: blog.csdn.net/weixin_45780132/article/details/112915407