1. 程式人生 > 實用技巧 >【模板】最小生成樹(prime、kruscal、prim堆優化)

【模板】最小生成樹(prime、kruscal、prim堆優化)

Description

給出一個無向圖,求出最小生成樹,如果該圖不連通,則輸出orz

Input

第一行包含兩個整數N、M,表示該圖共有N個結點和M條無向邊(N<=5000,M<=200000)
接下來M行每行包含三個整數Xi、Yi、Zi,表示有一條長度為Zi的無向邊連線結點Xi、Yi

Output

輸出包含一個數,即最小生成樹的各邊的長度之和;如果該圖不連通則輸出orz

Sample Input

4 5
1 2 2
1 3 2
1 4 3
2 3 4
3 4 3

Sample Output

7

Hint

【資料規模】
對於20%的資料:N<=5,M<=20
對於40%的資料:N<=50,M<=2500
對於70%的資料:N<=500,M<=10000
對於100%的資料:N<=5000,M<=200000


0、題外話

  • 關於三種演算法:
  • prim常用於稠密圖,kruscal用於稀疏圖,而prim堆優化在稀疏圖中比kruskal跑得慢,在稠密圖中用prim優化有點雞肋。。。
  • 演算法講解

1、Prime演算法

#include <iostream>
#include <cstdio>
#define inf 0x7fffffff
using namespace std;
int n,m,cnt,head[5005];
struct node{int next,to,w;}e[400005];
void addedge(int x,int y,int z){e[++cnt].to=y,e[cnt].w=z,e[cnt].next=head[x],head[x]=cnt;}
int flag[5005],dist[5005];
void prim()
{
	for(int i=1;i<=n;++i) dist[i]=inf; dist[1]=0;
	int ans=0;
	for(int i=1;i<=n;++i)
	{
		int minn=inf,k=-1;
		for(int j=1;j<=n;++j)
			if(dist[j]<minn&&!flag[j]) minn=dist[j],k=j;
		if(k==-1){puts("orz"); return;}
		flag[k]=1; ans+=dist[k];
		for(int j=head[k];j;j=e[j].next)
			if(!flag[e[j].to]&&dist[e[j].to]>e[j].w) dist[e[j].to]=e[j].w;
	}
	printf("%d\n",ans);
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1,x,y,z;i<=m;++i) scanf("%d%d%d",&x,&y,&z),addedge(x,y,z),addedge(y,x,z);
	prim();
	return 0;
}

2、Kruskal演算法

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
int n,m,ans,cnt,fa[5005];
struct fdfdfd{int x,y,w;}e[200005];
int getfa(int x){return x==fa[x]?x:fa[x]=getfa(fa[x]);}
bool cmp(fdfdfd a,fdfdfd b){return a.w<b.w;}
void kruskal()
{
	sort(e+1,e+m+1,cmp);
	for(int i=1;i<=n;++i) fa[i]=i;
	for(int i=1;i<=m;++i)
	{
		int fx=getfa(e[i].x),fy=getfa(e[i].y);
		if(fx==fy) continue;
		fa[fx]=fy; ans+=e[i].w; ++cnt;
		if(cnt==n-1){printf("%d\n",ans); return;}
	}
	puts("orz");
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;++i) scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].w);
	kruskal();
	return 0;
}

3、堆優化prim演算法-STL實現

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
int cnt,n,m,ans,head[5005],dist[5005],flag[5005];
struct node{int to,w,next;} e[400005];
void addedge(int x,int y,int z){e[++cnt].to=y; e[cnt].w=z; e[cnt].next=head[x]; head[x]=cnt;}
typedef pair <int,int> pii;
priority_queue <pii,vector<pii>,greater<pii> > q;
void prim()
{
    memset(dist,0x7f,sizeof(dist)); dist[1]=0;
    cnt=0;
    q.push(make_pair(0,1));
    while(!q.empty()&&cnt<n)
    {
        int d=q.top().first,u=q.top().second;
		q.pop();
        if(flag[u]) continue;
        ++cnt; ans+=d; flag[u]=1;
        for(int i=head[u];i;i=e[i].next)
        	if(e[i].w<dist[e[i].to]) dist[e[i].to]=e[i].w,q.push(make_pair(e[i].w,e[i].to));
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1,x,y,z;i<=m;++i) scanf("%d%d%d",&x,&y,&z),addedge(x,y,z),addedge(y,x,z);
    prim();
    if(cnt==n) printf("%d\n",ans);
    else puts("orz");
}