# MST Minimum Spanning Tree(最小生成樹) ### 1.Kruskal’s Algorithm 利用貪心法,先對權重排序,從權重較大的開始,檢查新增的這條邊是否有環,沒有的話就利用並查集把這兩個點合併,並把權重加到sum,最後輸出就完成啦~ 1. disjoin set初始化 ```cpp= int dis[MAXN],num[MAXN],n,ma=1; void init(){ for(int i=0;i<n;i++){ dis[i]=i; num[i]=1; } } ``` 2. 依照權重排序 ```cpp= struct node{ int x,y,v; }; vector<node> node; bool cmp(node a,node b) {return a.w < b.w;}; sort(node.begin(),node.end(),cmp); ``` 3. 合併 ```cpp= bool uni(int a,int b){ if(fa(a)==fa(b)) return false; num[fa(a)]+=num[fa(b)]; ma=max(ma,num[fa(a)]); //檢查最大並查集有沒有包括全部的點,如果沒有代表此圖不是完全連通圖 dis[fa(b)]=fa(a); return true; } ``` 4. 主程式 ```cpp= int ans=0,cnt=0; for(node now:a){ if(uni(now.x,now.y)){ //合併成功,兩點屬於集合不同 ans+=now.w; } } if(ma==n) cout << ans << '\n'; else cout << -1 << '\n'; //不是完全連通圖 ``` ### 2.Prim's Algorithm d[i]陣列為當前i節點距離最小生成樹的最小距離 1. 初始化 2. 找到距離最小生成數權重最小的邊 3. 把最小生成樹加上該邊 4. 更新d陣列 ```cpp= int prim(int n){ int edge=1,sum=0; //初始化 for(int i=0;i<n;i++){ d[i]=1e9; used[i]=0; } d[0]=0; for(int i=0;i<n;i++){ //找距離最小生成樹的最小邊 int index,mi=INT_MAX; for(int j=0;j<n;j++){ if(!used[j] && d[j]<mi){ mi=d[j]; index=j; } } if(mi==INT_MAX) break; used[index]=true; sum+=mi; edge++; //更新鄰接新增的點距離最小生成數的權重 for(int j=0;j<n;j++){ if(!used[j] && map[index][j]<d[j]){ d[j]=map[index][j]; } } } return edge==n?sum:-1; } ``` set優化 ```cpp= priority_queue<pii,vector<pii>,greater<pii>> pq; //每次選距離MST最短距離的vertex pq.push({0,0}); int ans=0,cnt=n; while(!pq.empty()){ pii now=pq.top();pq.pop(); if(confirm[now.idx]) continue; ans+=now.val; confirm[now.idx]=true; //設為確定值 cnt--; for(auto i:Map[now.idx]){ if(!confirm[i.idx]){ pq.push({i.val,i.idx}); } } } if(cnt==0) cout << ans << '\n'; else cout << -1 << '\n'; //沒有所有點連通 ``` ##### 注意!!!! 與dijkstra差別在prim's要設confirm陣列,而Dijkstra則是設一個到該點最小距離的陣列 因為dijkstra是求單源到各個點的最短路徑,所以判斷式是這樣 ```cpp= if(now.val>dis[now.x][now.y]) continue; ``` 但prim's是求距離最小生成樹的距離,如果用dis陣列紀錄的話,每次新增一個元素都會改變最小生成樹的位置,所以應該用used陣列紀錄是否確定為最小生成樹 ```cpp= if(confirm[now.node]) continue; ``` ### 題目 <a href="https://judge.tcirc.tw/ShowProblem?problemid=d098">d098: P-7-12. 最小生成樹</a> //Kruskal’s Algorithm比較快,但相對的Code就比較持長 Kruskal’s Algorithm ```cpp= #include <bits/stdc++.h> using namespace std; #define MAXN 10005 struct node{ int x,y,w; }; int dis[MAXN],num[MAXN],n,m,x,y,w,ma=1; vector<node> a; bool cmp(node a,node b){ return a.w<b.w; } void init(){ for(int i=0;i<n;i++){ dis[i]=i; num[i]=1; } } int fa(int a){ if(dis[a]==a) return a; return dis[a]=fa(dis[a]); } bool uni(int a,int b){ if(fa(a)==fa(b)) return false; num[fa(a)]+=num[fa(b)]; ma=max(ma,num[fa(a)]); dis[fa(b)]=fa(a); return true; } int main(){ cin >> n >> m; init(); while(m--){ cin >> x >> y >> w; a.push_back({x,y,w}); } sort(a.begin(),a.end(),cmp); int ans=0,cnt=0; for(node now:a){ if(uni(now.x,now.y)){ ans+=now.w; } } if(ma==n) cout << ans << '\n'; else cout << -1 << '\n'; } ``` Prim's Algorithm ```cpp= ##include <bits/stdc++.h> using namespace std; #define MAXN 10005 #define pii pair<int,int> #define val first #define idx second int a[MAXN],dis[MAXN]; vector<pii> Map[MAXN]; bool confirm[MAXN]; int main(){ int n,m,x,y,w; cin >> n >> m; while(m--){ cin >> x >> y >> w; Map[x].push_back({w,y}); Map[y].push_back({w,x}); } priority_queue<pii,vector<pii>,greater<pii>> pq; pq.push({0,0}); int ans=0,cnt=n; while(!pq.empty()){ pii now=pq.top();pq.pop(); if(confirm[now.idx]) continue; ans+=now.val; confirm[now.idx]=true; cnt--; for(auto i:Map[now.idx]){ if(!confirm[i.idx]){ pq.push({i.val,i.idx}); } } } if(cnt==0) cout << ans << '\n'; else cout << -1 << '\n'; } ```