所謂生成樹,就是n個點之間連成n-1條邊的圖形。而最小生成樹,就是權值(兩點間直線的值)之和的最小值。
首先,要用二維數組記錄點和權值。如上圖所示無向圖:
int map[7][7];
map[1][2]=map[2][1]=4;
map[1][3]=map[3][1]=2;
......
然后再求最小生成樹。具體方法是:
1.先選取一個點作起始點,然后選擇它鄰近的權值最小的點(如果有多個與其相連的相同最小權值的點,隨便選取一個)。如1作為起點。
visited[1]=1;
pos=1;
//用low[]數組不斷刷新最小權值,low[i](0<i<=點數)的值為:i點到鄰近點(未被標記)的最小距離。
low[1]=0; //起始點i到鄰近點的最小距離為0
low[2]=map[pos][2]=4;
low[3]=map[pos][3]=2;
low[4]==map[pos][4]=3;
low[5]=map[pos][5]=MaxInt; //無法直達
low[6]=map[pos][6]=MaxInt;
2.再在伸延的點找與它鄰近的兩者權值最小的點。
//low[]以3作當前位置進行更新
visited[3]=1;
pos=3;
low[1]=0; //已標記,不更新
low[2]=map[1][2]=4; //比5小,不更新
low[3]=2; //已標記,不更新
low[4]=map[1][4]=3; //比1大,更新后為:low[4]=map[3][4]=1;
low[5]=map[1][5]=MaxInt;//無法直達,不更新
low[6]=map[1][6]=MaxInt;//比2大,更新后為:low[6]=map[3][6]=2;
3.如此類推...

所有權值相加就是最小生成樹,其值為2+1+2+4+3=12。
至於具體代碼如何實現,現在結合POJ1258例題解釋。代碼如下:
1 //poj-1258 2 #include <stdio.h> 3 #include <string.h> 4 #define MaxInt 0x3f3f3f3f 5 #define N 110 6 //創建map二維數組儲存圖表,low數組記錄每2個點間最小權值,visited數組標記某點是否已訪問 7 int map[N][N],low[N],visited[N]; 8 int n; 9 int prim() 10 { 11 int i,j,pos,min,result=0; 12 memset(visited,0,sizeof(visited)); 13 visited[1]=1;pos=1; //從某點開始,分別標記和記錄該點 14 for(i=1;i<=n;i++) //第一次給low數組賦值 15 if(i!=pos) low[i]=map[pos][i]; 16 for(i=1;i<n;i++) //再運行n-1次 17 { 18 min=MaxInt; //找出最小權值並記錄位置 19 for(j=1;j<=n;j++) 20 if(visited[j]==0&&min>low[j]) 21 { 22 min=low[j];pos=j; 23 } 24 result+=min; //最小權值累加 25 visited[pos]=1; //標記該點 26 for(j=1;j<=n;j++) //更新權值 27 if(visited[j]==0&&low[j]>map[pos][j]) 28 low[j]=map[pos][j]; 29 } 30 return result; 31 } 32 33 int main() 34 { 35 int i,v,j,ans; 36 while(scanf("%d",&n)!=EOF) 37 { 38 memset(map,MaxInt,sizeof(map)); //所有權值初始化為最大 39 for(i=1;i<=n;i++) 40 for(j=1;j<=n;j++) 41 { 42 scanf("%d",&v); 43 map[i][j]=map[i][j]=v; 44 } 45 ans=prim(); 46 printf("%d\n",ans); 47 } 48 return 0; 49 }
http://acm.nyist.net/JudgeOnline/problem.php?pid=38
nyoj—38代碼:

1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 6 int map[550][550],vis[550],low[550]; 7 int n; 8 int prim() 9 { 10 int i,j,pos,min,sum=0; 11 memset(low,0,sizeof(low)); 12 memset(vis,0,sizeof(vis)); 13 vis[1]=1;pos=1; 14 for(i=1;i<=n;i++) 15 if(i!=pos) 16 low[i]=map[pos][i]; 17 for(i=1;i<n;i++) 18 { 19 min=200; 20 for(j=1;j<=n;j++) 21 { 22 if(!vis[j]&&low[j]<min) 23 { 24 min=low[j]; 25 pos=j; 26 } 27 } 28 sum+=min; 29 vis[pos]=1; 30 for(j=1;j<=n;j++) 31 { 32 if(vis[j]==0&&low[j]>map[pos][j]) 33 low[j]=map[pos][j]; 34 } 35 } 36 return sum; 37 } 38 int main() 39 { 40 int T; 41 scanf("%d",&T); 42 while(T--) 43 { 44 int i,j,m,v,e,c; 45 int s[550]; 46 memset(map,0,sizeof(map)); 47 memset(s,0,sizeof(s)); 48 scanf("%d %d",&n,&m); 49 //if(n==0&&m==0) 50 //{ 51 // printf("0\n"); 52 // continue; 53 //} 54 for(i=0;i<=n;i++) 55 for(j=0;j<=n;j++) 56 map[i][j]=map[j][i]=200; 57 for(i=0;i<m;i++) 58 { 59 scanf("%d %d %d",&v,&e,&c); 60 map[v][e]=map[e][v]=c; 61 } 62 for(i=0;i<n;i++) 63 scanf("%d",&s[i]); 64 sort(s,s+n); 65 printf("%d\n",prim()+s[0]); 66 } 67 return 0; 68 } 69 //prim算法 70 //初始化時沒有把 vis數組清零導致 wa
最小生成樹Kruskal算法+並查集實現
今天剛掌握Kruskal算法,寫下隨筆。
對於稀疏圖來說,用Kruskal寫最小生成樹效率更好,加上並查集,可對其進行優化。
Kruskal算法的步驟:
1.對所有邊進行從小到大的排序。
2.每次選一條邊(最小的邊),如果如果形成環,就不加入(u,v)中,否則加入。那么加入的(u,v)一定是最佳的。
並查集:
我們可以把每個連通分量看成一個集合,該集合包含了連通分量的所有點。而具體的連通方式無關緊要,好比集合中的元素沒有先后順序之分,只有“屬於”與“不屬於”的區別。圖的所有連通分量可以用若干個不相交集合來表示。
而並查集的精妙之處在於用數來表示集合。如果把x的父結點保存在p[x]中(如果沒有父親,p[x]=x),則不難寫出結點x所在樹的遞歸程序:
find(int x) {return p[x]==x?x:p[x]=find(p[x]);}
意思是,如果p[x]=x,說明x本身就是樹根,因此返回x;否則返回x的父親p[x]所在樹的根結點。
既然每棵樹表示的只是一個集合,因此樹的形態是無關緊要的,並不需要在“查找”操作之后保持樹的形態不變,只要順便把遍歷過的結點都改成樹根的兒子,下次查找就會快很多了。如下圖所示:

1 //hdu_1863 2 #include <stdio.h> 3 #include <stdlib.h> 4 #include <algorithm> 5 #define N 150 6 using namespace std; 7 int m,n,u[N],v[N],w[N],p[N],r[N]; 8 int cmp(const int i,const int j) {return w[i]<w[j];} 9 int find(int x) {return p[x]==x?x:p[x]=find(p[x]);} 10 int kruskal() 11 { 12 int cou=0,x,y,i,ans=0; 13 for(i=0;i<n;i++) p[i]=i; 14 for(i=0;i<m;i++) r[i]=i; 15 sort(r,r+m,cmp); 16 for(i=0;i<m;i++) 17 { 18 int e=r[i];x=find(u[e]);y=find(v[e]); 19 if(x!=y) {ans += w[e];p[x]=y;cou++;} 20 } 21 if(cou<n-1) ans=0; 22 return ans; 23 } 24 25 int main() 26 { 27 int i,ans; 28 while(scanf("%d%d",&m,&n)!=EOF&&m) 29 { 30 for(i=0;i<m;i++) 31 { 32 scanf("%d%d%d",&u[i],&v[i],&w[i]); 33 } 34 ans=kruskal(); 35 if(ans) printf("%d\n",ans); 36 else printf("?\n",ans); 37 } 38 return 0; 39 }
我的hdu1863代碼:
題目鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=1863
1 #include <cstdio> 2 #include <iostream> 3 #include <cstring> 4 //#include <algotithm> 5 #include <stdlib.h> 6 using namespace std; 7 typedef struct IN 8 { 9 int a; 10 int b; 11 int c; 12 }IN; 13 IN s[5000]; 14 int N,M; 15 int pre[110]; 16 int cmp(const void *a,const void *b) 17 { 18 return (*(IN *)a).c - (*(IN *)b).c; 19 } 20 int find(int x) 21 { 22 int i,r,t; 23 r=x; 24 while(r!=pre[r]) 25 r=pre[r]; 26 while(x!=r) 27 { 28 i=pre[x]; 29 pre[x]=r; 30 x=i; 31 } 32 return r; 33 } 34 int kruskal() 35 { 36 int i,j,pa,pb,num=0,sum=0; 37 for(i=0;i<=M;i++) 38 pre[i]=i; 39 for(i=0;i<N;i++) 40 { 41 pa=find(s[i].a); 42 pb=find(s[i].b); 43 if(pa!=pb) 44 { 45 pre[pa]=pb; 46 sum+=s[i].c; 47 num++; 48 } 49 } 50 if(num==M-1) 51 return sum; 52 else 53 return 0; 54 } 55 int main() 56 { 57 while(scanf("%d %d",&N,&M),N) 58 { 59 int i,j,t; 60 memset(s,0,sizeof(s)); 61 for(i=0;i<N;i++) 62 scanf("%d %d %d",&s[i].a,&s[i].b,&s[i].c); 63 qsort(s,N,sizeof(s[0]),cmp); 64 //for(i=0;i<N;i++) 65 //printf("%d\n",s[i].c); 66 t=kruskal(); 67 if(t) 68 printf("%d\n",t); 69 else 70 printf("?\n"); 71 } 72 return 0; 73 }