グラフの最小全域木の重みを求めるアルゴリズム.
最小全域木とはもとのグラフの部分グラフでありかつすべての頂点を含む木のなかで辺の重みの総和が最小のもののことである. 同じく最小全域木を求めるアルゴリズムとして Prim 法 がある.
コストの小さい辺から(閉路ができないように)貪欲に取っていくことでアルゴリズムが正しく動くことが知られている. 証明はグラフ, マトロイドの両面から示せる.
グラフの側面から示した証明はよくあるので, 次の補足でマトロイドの側面から Kruskal 法の正当性を示してみた(⇒ 補足).
(関数)
add_edge$(u, v, cost)$ : $u, v$ の間に重み $cost$ の無向辺を追加する
solve(): 最小全域木の重みを計算する
時間計算量: $\O (m \log n)$
- class UnionFind {
- private:
- int sz;
- vector<int> par, nrank;
- public:
- UnionFind(){}
- UnionFind(int node_size) : sz(node_size), par(sz), nrank(sz, 0){
- iota(par.begin(), par.end(), 0);
- }
- int find(int x){
- if(par[x] == x) return x;
- else return par[x] = find(par[x]);
- }
- void unite(int x,int y){
- x = find(x), y = find(y);
- if(x == y) return;
- if(nrank[x] < nrank[y]) swap(x,y);
- par[y] = x;
- if(nrank[x] == nrank[y]) nrank[x]++;
- }
- bool same(int x,int y){
- return find(x) == find(y);
- }
- };
- template<typename T> class Kruskal{
- public:
- struct edge{
- int u,v;
- T cost;
- bool operator<(const edge& another) const {
- return cost < another.cost;
- }
- };
- vector<edge> es;
- int V;
- Kruskal(int node_size) : V(node_size){}
- void add_edge(int u,int v,T cost){
- es.push_back((edge){u,v,cost});
- }
- T solve(){
- UnionFind uf(V);
- T res = 0;
- int cnt = 0;
- sort(es.begin(),es.end());
- for(edge& e : es){
- if(!uf.same(e.u,e.v)){
- uf.unite(e.u,e.v);
- res += e.cost;
- cnt++;
- if(cnt == V-1){
- break;
- }
- }
- }
- return res;
- }
- };
AOJ : Minimum Spanning Tree 提出コード