最小生成樹(kruskal算法)

1、概述

最小生成樹問題顧名思義,歸納來講就是路修的最短。node

接下來引入幾個一看就明白的定義:ios

最小生成樹相關概念:算法

帶權圖:邊賦以權值的圖稱爲網或帶權圖,帶權圖的生成樹也是帶權的,生成樹T各邊的權值總和稱爲該樹的權。spa

最小生成樹(MST):權值最小的生成樹。.net

最小生成樹的性質:假設G=(V,E)是一個連通網,U是頂點V的一個非空子集。若(u,v)是一條具備最小權值的邊,其中u∈U,v∈V-U,則必存在一棵包含邊(u,v)的最小生成樹。3d

完成構造網的最小生成樹必須解決下面兩個問題:code

      (1)儘量選取權值小的邊,但不能構成迴路;blog

      (2)選取n-1條恰當的邊以連通n個頂點;排序

prim算法適合稠密圖,kruskal算法適合簡單圖。get

2、kruskal算法

kruskal遠離更爲簡單粗暴,可是須要藉助並查集這一知識。

(本人寫的一篇https://blog.csdn.net/qq_41754350/article/details/81271567

克魯斯卡爾算法的基本思想是以邊爲主導地位,始終選擇當前可用的最小邊權的邊(能夠直接快排或者algorithm的sort)。每次選擇邊權最小的邊連接兩個端點是kruskal的規則,並實時判斷兩個點之間有沒有間接聯通。

如今我來模擬一下:

假若有如下幾個城市,之間都有相連的道路:

根據kruskal的原理,咱們須要對邊權dis進行排序,每次找出最小的邊。

排序後,最小的邊天然是第8條邊,因而4和6相連。

遍歷繼續,第二小的邊是1號,1和2聯通。

再後來是邊3鏈接1,4。

dis也是14的還有邊5,它鏈接3,4。

其次是dis爲15的邊4,可是2和4已經相連了,pass。

而後是dis爲16的兩條邊(邊2和邊9),邊2鏈接1和3,邊9鏈接3和6,它們都已經間接相連,pass。

再而後就是dis爲22的邊10,它鏈接5和6,5尚未加入組織,因此使用這邊。繼續,發現此時已經鏈接了n-1條邊,結束,最後圖示以下:

 

原理如此簡單,代碼也很好實現(給個模板), 代碼以下所示(注意細節):

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,tot=0,k=0;//n端點總數,m邊數,tot記錄最終答案,k已經鏈接了多少邊 
int fat[200010];//記錄集體老大 
struct node
{
	int from,to,dis;//結構體儲存邊 
}edge[200010];
bool cmp(const node &a,const node &b)//sort排序(固然你也能夠快排) 
{
	return a.dis<b.dis;
}
int father(int x)//找集體老大,並查集的一部分 
{
	if(fat[x]!=x)
	return father(fat[x]);
	else return x;
}
void unionn(int x,int y)//加入團體,並查集的一部分 
{
	fat[father(y)]=father(x);
}
int main()
{
	scanf("%d%d",&n,&m);//輸入點數,邊數 
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&edge[i].from,&edge[i].to,&edge[i].dis);//輸入邊的信息 
	}
	for(int i=1;i<=n;i++) fat[i]=i;//本身最開始就是本身的老大 (初始化) 
	sort(edge+1,edge+1+m,cmp);//按權值排序(kruskal的體現) 
	for(int i=1;i<=m;i++)//從小到大遍歷 
	{
		if(k==n-1) break;//n個點須要n-1條邊鏈接 
		if(father(edge[i].from)!=father(edge[i].to))//假如不在一個團體 
		{
			unionn(edge[i].from,edge[i].to);//加入 
			tot+=edge[i].dis;//記錄邊權 
			k++;//已鏈接邊數+1 
		}
	}
	printf("%d",tot);
	return 0;
}