“本模塊關聯知識點:並查集”
首先先引入帶權無向圖的概念:所謂的帶權無向圖,就是無向圖的邊都有權值。
最小生成樹即保證每個節點在聯通並且沒有回路的狀態下邊的權值之和最小
最小生成樹有兩種解決方法:1、prime算法;2、kruskal算法;本篇講解kruskal算法
kruskal算法相較於prime算法更暴力,核心思想是利用快排不斷取最小權值的邊並通過並查集維護防止產生回路(見下圖)
圖中最終可得出最小生成樹的權值為2+1+1+1+3=7
代碼實現(以洛谷P3366為例)
題目描述
如題,給出一個無向圖,求出最小生成樹,如果該圖不連通,則輸出orz
輸入輸出格式
輸入格式:
第一行包含兩個整數N、M,表示該圖共有N個結點和M條無向邊。(N<=5000,M<=200000)
接下來M行每行包含三個整數Xi、Yi、Zi,表示有一條長度為Zi的無向邊連接結點Xi、Yi
輸出格式:
輸出包含一個數,即最小生成樹的各邊的長度之和;如果該圖不連通則輸出orz
輸入輸出樣例
輸入樣例
4 5
1 2 2
1 3 2
1 4 3
2 3 4
3 4 3
輸出樣例
7
本題與上圖例題相似,orz輸出的判定根據樹的節點之間邊的數量為節點數-1,這里直接寫出實現代碼。
#include
#include
#include
#include
using namespace std;
struct edge
{
int from,to;
long long val;
} e[200005];
int pa[5005];
bool cmp(edge a,edge b)
{
return a.val < b.val;
}
int find_root(int x)
{
if(pa[x] == x)
return x;
return x=find_root(pa[x]);
}
bool vertices_union(int x,int y)
{
int x_root=find_root(x);
int y_root=find_root(y);
if(x_root != y_root)
{
pa[x_root]=y_root;
return true;
}
else
return false;
}
int main()
{
int n,m;
long long ans=0;
cin >> n >> m;
for(int i=0; i <= n; i++)
pa[i]=i;
for(int i=1; i <= m; i++)
scanf("%d%d%lld",&e[i].from,&e[i].to,&e[i].val);
sort(e+1,e+m+1,cmp);
int cont=0;
for(int i=1; i <= m; i++)
{
if(vertices_union(e[i].from,e[i].to))
{
ans+=e[i].val;
cont++;
}
if(cont == n-1)
break;
}
if(cont < n-1)
cout << "orz" << endl;
else
cout << ans << endl;
return 0;
}