1. 程式人生 > >最小生成樹之Kruskal演算法

最小生成樹之Kruskal演算法

簡介

求加權連通圖的最小生成樹的演算法

演算法描述

先按邊從小到大排序,從最短邊開始迴圈,若此邊連線的兩個點屬於不同的聯通分量則加入此邊並連線兩點(用並查集實現),直到加入n-1條邊。若m條邊都迴圈一遍了加入的邊仍不足n-1則此圖不能構成一棵樹。

證明

反證法可證:設目前的最短邊k連線定點u,v,若不連此邊,有兩種情況:1)後面的邊中沒有能使u,v聯通的,則k必選。2)不選k仍能構成一棵樹,這時連線邊k則構成一個環,此時可去掉除k外的一邊得到一棵權值和更小的樹,前後矛盾。

程式碼

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>

using namespace std ;

const int N=5000+1000 ;
int n,m,tot,fa[N] ;
struct T{int x,y,info;}Edge[200005];

int Find(int x){
	return fa[x]==x?x:fa[x]=Find(fa[x]) ;
}

bool cmp(T a,T b){return a.info<b.info;}

int Kruskal(){
	int ans=0 ;
	for (int i=1;i<=n;i++) fa[i]=i ;
	for (int i=1;i<=m;i++)
		scanf("%d%d%d",&Edge[i].x,&Edge[i].y,&Edge[i].info) ;
	sort(Edge+1,Edge+1+m,cmp) ;
	for (int i=1;i<=m;i++){
		int RX=Find(Edge[i].x), RY=Find(Edge[i].y) ;
		if ( RX!=RY ){
			tot ++ ;
			fa[RX]=RY ;
			ans += Edge[i].info ;
		}
	}
	return tot==n-1?ans:-1 ;
}

int main(){
	scanf("%d%d",&n,&m) ;
	int ans = Kruskal() ;
	if ( ans==-1 ) printf("orz") ;
	else printf("%d",ans ) ;
}