背景:
無向連通圖G=(V,E)的最小產生樹演算法:
1.prim演算法:
(1)初始頂點集合S為空白,把任意一個頂點s加入集合S.
(2)迭代|V|-1次,每次加入頂點u,使得v屬於S,u不屬於S並且(u,v)權值最小, 記錄邊(u,v)
(3)所有記錄過的邊(u,v)即最小產生樹
2.kruskal演算法:
(1)把有|E|中邊按權值非降排序
(2)邊初始集合S為空白,每次加入權值最小的邊,當加入後邊集合不構成迴路,刪掉此邊。迴路判定可以採取並查集方法
(3)邊集合為最小產生樹
題意:求n個村莊的最小產生樹,1<n<27,n個村莊用前n個大寫字母表示。輸入n和 n-1個頂點與之連通的頂點和權值。輸入最小產生樹邊權值和。邊權值小於100,邊集合|E|小於等於75,一個頂點串連的邊數小於等於15.
分析:prim演算法對於處理稠密鄰接矩陣有效,而kruskal對於邊疏鬆陣列有效。這道題應該kruskal更有效一些。不過兩個演算法在Judge上運行都是Memory 228K, 時間為0MS,不知道那些Memory<30K是怎麼最佳化的。
源碼:
#include <iostream><br />using namespace std;</p><p>const int INF = 101;</p><p>struct node{<br />node():id(0),dis(0),next(NULL){}<br />node(char i,char d):id(i),dis(d),next(NULL){}<br />char id;<br />char dis;<br />struct node * next;<br />};</p><p>/*a為鄰接表*/<br />int kruskal(node *a, int n)<br />{<br />char *set = new char[n];/*並查集*/<br />char top = 0;/*並查集構造時參數*/<br />int c, sum, src;<br />node *it, *des, init(0,INF);</p><p>c = sum = 0;<br />memset(set,0,sizeof(char)*n);<br />set[top++]='A' + top;<br />while(c!=n-1){<br />des = &init;//初始距離為INF<br />for(int i=0;i<n-1;i++){//迭代鄰接表所有邊找未用過並且權值最小的邊<br />it = &a[i];<br />while(it->next!=NULL){<br />if(des->dis > it->next->dis && it->next->id!=INF){<br />src = i;<br />des = it->next;<br />}<br />it=it->next;<br />}<br />}<br />if(set[src]!=0 && set[des->id]!=0){//如果兩個頂點都在集合中<br />if(set[src] != set[des->id]){/*無迴路*/<br />for(int i=0;i<n;i++) if(set[i]==set[des->id] && i!=des->id)//合并集合<br />set[i] = set[src];<br />set[des->id]=set[src];<br />des->id=INF;sum +=des->dis;c++;//邊置為無效,求最小產生樹邊權和,邊數加一<br />}else//有迴路<br />des->id=INF;<br />}else if(set[src]==0 && set[des->id]!=0){<br />set[src]=set[des->id];//加入集合<br />des->id=INF;sum +=des->dis;c++;<br />}else if(set[src]!=0 && set[des->id]==0){<br />set[des->id]=set[src];//加入集合<br />des->id=INF;sum +=des->dis;c++;<br />}else if(!set[src]&&!set[des->id]){<br />set[src]=set[des->id]='A'+top++;//建立新集合<br />des->id=INF;sum +=des->dis;c++;<br />}<br />}<br />delete [] set;<br />return sum;<br />}</p><p>int main()<br />{<br />int num;<br />while(true){<br />/*建圖*/<br />cin >> num;<br />if(num==0) break;<br />node* adj = new node[num-1];<br />for(int i=0;i<num-1;i++){<br />int n, dis;<br />char src, des;<br />cin >> src >> n;<br />for(int j=0;j<n;j++){<br />cin >> des >> dis;<br />node* tmp=&adj[src-'A'];<br />while(tmp->next!=NULL)<br />tmp = tmp->next;<br />tmp->next = new node(des-'A',dis);<br />}<br />}<br />/*kruskal求最小產生樹,adj為鄰接表,num為頂點數目*/<br />cout << kruskal(adj,num) << endl;<br />/*釋放申請空間*/<br />for(int i=0;i<num-1;i++){<br />node *tmp= adj[i].next,*tmp1;<br />while(tmp!=NULL){<br />tmp1=tmp;<br />tmp=tmp->next;<br />delete tmp1;<br />}<br />}<br />delete [] adj;<br />}<br />return 0;<br />}<br />
//prim演算法<br />#include <iostream><br />using namespace std;</p><p>const char N = 26;<br />const char INF = 101;</p><p>int prim(char *a, int n)<br />{<br />int sum = 0;<br />char i,j,c=n;<br />char min, src,des;<br />bool *is = new bool[n];<br />memset(is,0,sizeof(bool)*n);<br />is[0]=true;<br />sum = 0;<br />while(--c){<br />min = INF;<br />for(i=0;i<n;i++) if(true == is[i])<br />for(j=0;j<n;j++) if(false == is[j])<br />if(min > a[i*n+j]){<br />min = a[i*n+j];<br />src = i;<br />des = j;<br />}<br />is[des]=true;<br />sum += a[src*n+des];<br />}<br />delete [] is;<br />return sum;<br />}</p><p>int main()<br />{<br />int t, dis, n;<br />char *adj;<br />char src,des;</p><p>while(true){<br />cin >> t;<br />adj = new char[t*t];<br />memset(adj,INF,t*t*sizeof(char));<br />if(t==0) break;<br />for(int i=0;i<t-1;i++){<br />cin >> src >> n;<br />for(int j=0;j<n;j++){<br />cin >> des >> dis;<br />adj[(src-'A')*t+(des-'A')] = dis;<br />adj[(des-'A')*t+(src-'A')] = dis;<br />}<br />}<br />cout << prim(adj,t) << endl;<br />delete [] adj;<br />}<br />return 0;<br />}<br />