# Kruskal's Algorithm# Disjoint Set Union# Greedy# Graph

a129 - 最小生成樹

🔗 前往 ZeroJudge 原題

題目描述

題目要求計算給定加權無向圖的最小生成樹 (MST) 的權重和。如果圖不連通,則輸出 -1。

解題思路

本題採用 Kruskal's Algorithm 求解最小生成樹。Kruskal's Algorithm 是一種貪心演算法,其步驟如下:

  1. 將所有邊按照權重從小到大排序。
  2. 初始化一個 Disjoint Set Union (DSU) 資料結構,每個頂點都屬於自己的集合。
  3. 遍歷排序後的邊,對於每一條邊 (u, v):
    • 如果 u 和 v 屬於不同的集合,則將它們合併到同一個集合,並將該邊的權重加到 MST 的權重和中。
  4. 如果 MST 包含 n-1 條邊(其中 n 是頂點數),則 MST 存在,輸出權重和。否則,圖不連通,輸出 -1。

Disjoint Set Union (DSU) 資料結構用於追蹤圖的連通性。它提供了兩個主要操作:

  • find(x): 找到包含頂點 x 的集合的代表元素。
  • union(x, y): 將包含頂點 x 和 y 的集合合併。

複雜度分析

  • 時間複雜度: O(E log E),其中 E 是邊的數量。排序邊需要 O(E log E) 的時間,遍歷邊和執行 DSU 操作需要 O(E α(V)) 的時間,其中 V 是頂點的數量,α(V) 是反阿克曼函數,增長非常慢,可以視為常數。
  • 空間複雜度: O(V + E),其中 V 是頂點的數量,E 是邊的數量。需要 O(V) 的空間來儲存 DSU 資料結構,需要 O(E) 的空間來儲存邊。

程式碼

#include <iostream>
#include <algorithm>
using namespace std;
int n,m,s[100001];
struct p{
	int x,y,v;
};
p a[10000001];
bool cmp(p xx,p yy){
	if(xx.v<yy.v||(xx.v==yy.v&&xx.y<yy.y)||(xx.v==yy.v&&xx.y==yy.y&&xx.x<yy.x))return 1;
	return 0;
}
int fd(int xx){
	if(s[xx]==xx)return xx;
	return s[xx]=fd(s[xx]);
}
void un(int xx,int yy){
	s[yy]=fd(s[xx]);
}
int main(){
	cin.sync_with_stdio(false), cin.tie(0);
	while(cin >> n >> m){
		for(int i=0;i<n;++i){
			s[i]=i;
		}
		for(int i=0;i<m;++i){
			cin >> a[i].x >> a[i].y >> a[i].v;
		}
		sort(a,a+m,cmp);
		long long int num=0,ans=0;
		for(int i=0;i<m;++i){
			//cout << a[i].x << " " << a[i].y << " " << a[i].v << "\n"; 
			int fdx=fd(a[i].x),fdy=fd(a[i].y);
			if(fdx!=fdy){
				un(fdx,fdy);
				++num;
				ans+=a[i].v;
			}
		}
		if(num==n-1){
			cout << ans << '\n';
		}
		else{
			cout << "-1\n";
		}
	}
}

Discussion