1. 程式人生 > >Prim算法---最小生成樹

Prim算法---最小生成樹

lld size stream queue truct type 個數 c89 tro

最小生成樹的Prim算法也是貪心算法的一大經典應用。Prim算法的特點是時刻維護一棵樹,算法不斷加邊,加的過程始終是一棵樹。

Prim算法過程:

一條邊一條邊地加, 維護一棵樹。
初始 E = {}空集合, V = {任意節點}

循環(n – 1)次,每次選擇一條邊(v1,v2), 滿足:v1屬於V , v2不屬於V。且(v1,v2)權值最小。

E = E + (v1,v2)
V = V + v2

最終E中的邊是一棵最小生成樹, V包含了全部節點。 以下圖為例介紹Prim算法的執行過程。 技術分享 Prim算法的過程從A開始 V = {A}, E = {}
技術分享
選中邊AF , V = {A, F}, E = {(A,F)}
技術分享

選中邊FB, V = {A, F, B}, E = {(A,F), (F,B)}
技術分享
選中邊BD, V = {A, B, F, D}, E = {(A,F), (F,B), (B,D)}
技術分享
選中邊DE, V = {A, B, F, D, E}, E = {(A,F), (F,B), (B,D), (D,E)}
技術分享
選中邊BC, V = {A, B, F, D, E, c}, E = {(A,F), (F,B), (B,D), (D,E), (B,C)}, 算法結束。 最後,我們來提供輸入輸出數據,由你來寫一段程序,實現這個算法,只有寫出了正確的程序,才能繼續後面的課程。 輸入
第1行:2個數N,M中間用空格分隔,N為點的數量,M為邊的數量。(2 <= N <= 1000, 1 <= M <= 50000)
第2 - M + 1行:每行3個數S E W,分別表示M條邊的2個頂點及權值。(1 <= S, E <= N,1 <= W <= 10000)
輸出
輸出最小生成樹的所有邊的權值之和。
輸入示例
9 14
1 2 4
2 3 8
3 4 7
4 5 9
5 6 10
6 7 2
7 8 1
8 9 7
2 8 11
3 9 2
7 9 6
3 6 4
4 6 14
1 8 8

輸出示例
37
請選取你熟悉的語言,並在下面的代碼框中完成你的程序,註意數據範圍,最終結果會造成Int32溢出,這樣會輸出錯誤的答案。 不同語言如何處理輸入輸出,請查看下面的語言說明。 簡單的最小生成樹,自己寫的代碼一直過不了 AC代碼(prim算法)
#include<iostream>  
#include<cstdio>  
#include<map>  
#include<cstring>  
#include<string>  
#include<algorithm>  
#include<queue>  
#include<vector>  
#include<stack>  
#include<cstdlib>  
#include<cctype>  
#include<cstring>  
#include<cmath>  
using namespace std;  
const int inf=0x3f3f3f3f;  
int G[1001][1001];//鄰接矩陣  
int vis[1001],lowc[1001];  
int prim(int G[][1001],int n){  
    int i,j,p,minc,res=0;  
    memset(vis,0,sizeof(vis));  
    vis[1]=1;//從1開始訪問  
    for(i=2;i<=n;i++) lowc[i]=G[1][i];  
    for(i=2;i<=n;i++){  
        minc=inf; p=-1;  
        for(j=1;j<=n;j++){  
            if(vis[j]==0&&lowc[j]<minc){  
                minc=lowc[j]; p=j;  
            }  
        }  
        //cout<<minc<<endl;  
        if(inf==minc) return -1;//原圖不聯通  
        res+=minc; vis[p]=1;  
        for(j=1;j<=n;j++){//更新lowc[]  
            if(vis[j]==0&&lowc[j]>G[p][j]){  
                lowc[j]=G[p][j];  
            }  
        }  
    }  
    return res;  
}  
  
int main()  
{  
    int n,m;  
    while(cin>>n>>m){  
        int x,y,w;  
        memset(G,inf,sizeof(G));//首先記錄所有邊的權為inf  
        for(int i=1;i<=m;i++){  
            cin>>x>>y>>w;  
            G[x][y]=G[y][x]=w;  
            //cout<<G[x][y]<<endl;  
        }  
        //int res=prim(n);  
        cout<<prim(G,n)<<endl;  
    }  
    return 0;  
}  

  

自己寫的代碼不知道錯哪了(kruskal算法)
#include<stdio.h>
#include<string.h>
#include<algorithm>

using namespace std;

struct tr
{
	int s,e,w;
}p[50000+10];

bool cmp(tr x, tr y)
{
	return x.w < y.w;
}

int n,m;
int f[1000+10];
int i,j;
long long ans;

int find(int x)  //找父親 
{  
	int r = x;
	while(f[r] != r)
	r = f[r];
	return r;
	int i = x, j;
	while(i != r)
	{
		j = f[i];
		f[i] = r;
		r = j;
	}   
} 

void join(int x, int y)
{
	int fx = find(x);
	int fy = find(y);
	if(fx != fy)
		f[fx] = fy;
} 

int kruskal()
{
	sort(p, p+m, cmp);
	for(i=0; i<n; i++)
	{
		f[i] == i;//初始化 父親節點 
	}
	for(i=0; i<n; i++)
	{
		if(find(p[i].s) != find(p[i].e))
		{
			join(p[i].e, p[i].s);
			ans += p[i].w;
		}
	}
	return ans;
}

int main()
{
	ans=0;
	scanf("%d %d",&n,&m);
	for(i=0; i<m; i++)
	{
		scanf("%d %d %d",&p[i].s, &p[i].e, &p[i].w);
	}
	kruskal();
	printf("%lld\n",ans);
	return 0;
 } 

  

Prim算法---最小生成樹