克魯斯卡爾算法:Kruskal算法是一種用來查找最小生成樹的算法,由Joseph Kruskal在1956年發表。用來解決同樣問題的還有Prim算法和Boruvka算法等。三種算法都是貪心算法的應用。和Boruvka算法不同的地方是,Kruskal算法在圖中存在相同權值的邊時也有效。
基本思想:先構造一個只含 n 個頂點、而邊集為空的子圖,把子圖中各個頂點看成各棵樹上的根結點,之后,從網的邊集 E 中選取一條權值最小的邊,若該條邊的兩個頂點分屬不同的樹,則將其加入子圖,即把兩棵樹合成一棵樹,反之,若該條邊的兩個頂點已落在同一棵樹上,則不可取,而應該取下一條權值最小的邊再試之。依次類推,直到森林中只有一棵樹,也即子圖中含有 n-1 條邊為止。
發現一個好的視頻:https://www.bilibili.com/video/BV1Eb41177d1
下圖為初始圖、只含有點的森林和點與點之間的聯系
循環找權值最小的邊
依次向下循環...
輸入:
6 10
1 2 6
1 3 1
1 4 5
2 3 5
2 5 3
3 4 5
3 5 6
3 6 4
4 6 2
5 6 6
輸出:
V1-V3=1
V4-V6=2
V2-V5=3
V3-V6=4
V5-V6=5
15
代碼:
#include <iostream> #include <bits/stdc++.h> using namespace std; #define MAX 100 int Find(int parent[],int i) { while(parent[i]>0) { i=parent[i]; } return i; } void Kruskal(int u[],int v[],int w[],int n,int m) { int parent[MAX]; int sum=0; for(int i=1;i<=n;i++) //初始化 { parent[i]=0; } int a,b; for(int i=1;i<=m;i++) { a=Find(parent,u[i]); b=Find(parent,v[i]); if(a!=b) //a==b說明成環 { parent[a]=b; cout<<"V"<<a<<"-"<<"V"<<b<<"="<<w[i]<<endl; sum+=w[i]; } } cout<<sum; } int main() { int n,m; int u[MAX],v[MAX],w[MAX]; cin>>n>>m; for(int i=1;i<=m;i++) { cin>>u[i]>>v[i]>>w[i]; } for(int i=1;i<=m;i++) //排序 { int min=i; for(int j=i+1;j<=m;j++) { if(w[min]>w[j]) { min=j; } } swap(u[i],u[min]); swap(v[i],v[min]); swap(w[i],w[min]); } Kruskal(u,v,w,n,m); return 0; }