最小生成樹是樹的一種,具體有兩種算法去實現。ios
普利姆(prim)算法:web
大意:先找一個數(通常爲1或輸入的第一個數)將其視爲一個集合m,那麼剩下的集合可看作(u(設爲總集合)-m)。算法
接着找出一個最短邊(在鏈接兩個集合的邊中),而後將這邊納入集合m,直到m=u;測試
例子:spa
某省調查鄉村交通情況,獲得的統計表中列出了任意兩村莊間的距離。省政府「暢通工程」的目標是使全省任何兩個村莊間均可以實現公路交通(但不必定有直接的公路相連,只要能間接經過公路可達便可),並要求鋪設的公路總長度爲最小。請計算最小的公路總長度。
code
Inputorm
測試輸入包含若干測試用例。每一個測試用例的第1行給出村莊數目N ( < 100 );隨後的N(N-1)/2行對應村莊間的距離,每行給出一對正整數,分別是兩個村莊的編號,以及此兩村莊間的距離。爲簡單起見,村莊從1到N編號。
當N爲0時,輸入結束,該用例不被處理。
排序
Outputstring
對每一個測試用例,在1行裏輸出最小的公路總長度。
it
Sample Input
3 1 2 1 1 3 2 2 3 4 4 1 2 1 1 3 4 1 4 1 2 3 3 2 4 2 3 4 5 0
Sample Output
3 5
code:
#include <stdio.h>
#include <string.h>
#define MaxInt 65535//設置最大值 表示兩個點不通
#define N 110
int map[N][N],low[N],visited[N];
int n;
int prim()
{
int i,j,pos,min,result=0;
memset(visited,0,sizeof(visited));
visited[1]=1;//遍歷過就標記爲一
pos=1;//記錄m集合的「外交官」
for(i=1; i<=n; i++)
if(i!=pos)
low[i]=map[pos][i];//記錄和pos點邊距離的值
for(i=1; i<n; i++)//固然要算的是整個頂點
{
min=MaxInt;
for(j=1; j<=n; j++)
if(visited[j]==0&&min>low[j])
{
min=low[j];
pos=j;
}//找出最短邊
result+=min;
visited[pos]=1;//歸入m集合.
for(j=1; j<=n; j++)
if(visited[j]==0&&low[j]>map[pos][j])
low[j]=map[pos][j];//記錄和pos點邊距離的值
}
return result;
}
int main()
{
int i,v,j,ans,a,b;
while(scanf("%d",&n)!=EOF)
{
if(n==0)
break;
memset(map,MaxInt,sizeof(map));//初始值 視每兩個點都不通
for(j=1; j<=n*(n-1)/2; j++)
{
scanf("%d%d%d",&a,&b,&v);
map[a][b]=map[b][a]=v;//標記兩個城市間的距離
}
ans=prim();
printf("%d\n",ans);
}
return 0;
}
2 克魯斯卡爾(kruskal)算法:
大意:
咱們假設一個圖有m個節點,n條邊。首先,咱們須要把m個節點當作m個獨立的生成樹,而且把n條邊按照從小到大的數據進行排列。在n條邊中,咱們依次取出其中的每一條邊,若是發現邊的兩個節點分別位於兩棵樹上,那麼把兩棵樹合併成爲一顆樹;若是樹的兩個節點位於同一棵樹上,那麼忽略這條邊,繼續運行。等到全部的邊都遍歷結束以後,若是全部的生成樹能夠合併成一條生成樹,那麼它就是咱們須要尋找的最小生成樹,反之則沒有最小生成樹。ps 要用並查集的思想
例題:
某省調查鄉村交通情況,獲得的統計表中列出了任意兩村莊間的距離。省政府「暢通工程」的目標是使全省任何兩個村莊間均可以實現公路交通(但不必定有直接的公路相連,只要能間接經過公路可達便可),並要求鋪設的公路總長度爲最小。請計算最小的公路總長度。
Input
測試輸入包含若干測試用例。每一個測試用例的第1行給出村莊數目N ( < 100 );隨後的N(N-1)/2行對應村莊間的距離,每行給出一對正整數,分別是兩個村莊的編號,以及此兩村莊間的距離。爲簡單起見,村莊從1到N編號。
當N爲0時,輸入結束,該用例不被處理。
Output
對每一個測試用例,在1行裏輸出最小的公路總長度。
Sample Input
3 1 2 1 1 3 2 2 3 4 4 1 2 1 1 3 4 1 4 1 2 3 3 2 4 2 3 4 5 0
Sample Output
3 5
code:
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define M 5000
int Start[M],End[M],Wealth[M],father[M],line[M];
int find_(int f)//查找父節點
{
if(f!=father[f])
return father[f]=find_(father[f]);
return f;
}
int cmp(const int i,const int j)//sort排序要加的東西,具體啥用不清楚。。。
{
if(Wealth[i]!=Wealth[j])
return Wealth[i]<Wealth[j];//按邊值排序
}
int kruskal(int n,int m)//具體實現步驟
{
int e,ans=0,i,j,x,y;
for(i=0;i<=n;i++)//初始化爲單一集合
father[i]=i;
for(i=0;i<=m;i++)
line[i]=i;
sort(line,line+m,cmp);//按邊值排序
for(i=0;i<m;i++)
{
e=line[i],x=find_(Start[e]),y=find_(End[e]);
if(x!=y)//判斷是否兩個節點位於同一個樹上
{
father[x]=y;
ans+=Wealth[e];
}
}
return ans;
}
int main()
{
int n,m,i,p;
while(1)
{
scanf("%d",&n);
if(n==0)
break;
m=n*(n-1)/2;
for(i=0;i<m;i++)
scanf("%d%d%d",&Start[i],&End[i],&Wealth[i]);
p=kruskal(n,m);
printf("%d\n",p);
}
}
總結:
1.普里姆(Prim)算法
特色:時間複雜度爲O(n2).適合於求邊稠密的最小生成樹。
2.克魯斯卡爾(Kruskal)算法
特色:時間複雜度爲O(eloge)(e爲網中邊數),適合於求稀疏的網的最小生成樹。