按照慣例,接下來是本篇目錄:
$1 什么是最小生成樹?
$2 什么是克魯斯卡爾算法?
$3 克魯斯卡爾算法的例題
摘要:本片講的是最小生成樹中的玄學算法--克魯斯卡爾算法,然后就沒有然后了。
$1 什么是最小生成樹?
•定義:
先引入一個定理:N個點用N-1條邊連接成一個聯通塊,形成的圖形只可能是樹,沒有別的可能;
根據這個定理,我們定義:在一個有N個點的圖中,選出N-1條邊出來,連接所有N個點,這N-1條邊的邊權之和最小的方案;
•最小生成樹之prim算法:
由於本蒟蒻還不會這個算法,所以暫時將這個算法放在這里,講講思路,代碼實在不會打QAQ
算法思路:
1. 從圖中選取一個節點作為起始節點(也是樹的根節點),標記為已達;初始化所有未達節點到樹的距離為到根節點的距離;
2. 從剩余未達節點中選取到樹距離最短的節點i,標記為已達;更新未達節點到樹的距離(如果節點到節點i的距離小於現距離,則更新);
3. 重復步驟2直到所有n個節點均為已達。
$2 什么是克魯斯卡爾算法?
接下來是正題--克魯斯卡爾算法
•算法思路:
(1)將所有邊的邊權從小到大依次排列,並且均標為未選;
(2)選擇最小的未選邊;
(3)如果該邊與前面所選的邊無法構成回路,則選中該邊,並標為已選;如果該邊與前面所選的邊構成了回路,則不選該邊,並標為已選;
(4)重復(2)(3),直到所有點之間都有邊相連;
•舉個栗子:
以下面這個圖為例:
將各條邊排序可得 3-4-5-6-6-7-8-9-12;
首先將最小的的邊選上,即2--3,如圖:
接下來,將第二條邊選上,即1--2,如圖:
第三條邊:
第四條邊是6,但是與前三條邊構成了回路,不選它;
第五條邊:
第六條邊:
最后一條邊:
•代碼實現:
1 struct point 2 { 3 int x;//始邊 4 int y;//終邊 5 int v;//邊的權值 6 }; 7 point a[9901]; 8 int fat[101]; 9 int n,i,j,x,m,tot,k; 10 int father(int x)//並查集中的查找 11 { 12 if(fat[x]!=x) fat[x]=father(fat[x]); 13 return fat[x]; 14 } 15 16 void unionn(int x,int y)//並查集中的合並 17 { 18 int fa=father(x); 19 int fb=father(y); 20 if(fa!=fb) fat[fa]=fb; 21 } 22 23 int cmp(const point &a,const point &b) 24 { 25 if(a.v<b.v) return 1;//對邊的權值進行排序 26 else return 0; 27 } 28 29 int main() 30 { 31 cin>>n; 32 for(int i=1;i<=n;++i) 33 for(int j=1;j<=n;++j) 34 { 35 cin>>x; 36 if(x!=0) 37 { 38 m++; 39 a[m].x=i;a[m].y=j;a[m].v=x; 40 } 41 } 42 for(int i=1;i<=n;++i) fat[i]=i; 43 sort(a+1,a+m+1,cmp); 44 for(int i=1;i<=m;++i) 45 { 46 if(father(a[i].x)!=father(a[i].y)) 47 { 48 unionn(a[i].x,a[i].y); 49 tot+=a[i].v; 50 k++; 51 }//如果不能構成一個聯通塊,就將現在的這條邊加入並查集 52 if(k==n-1) break;//否則將現在的這條邊撇開不管 53 } 54 cout<<tot; 55 return 0; 56 }
神仙們想必都已經看出來了,克魯斯卡爾算法用到了並查集的思想(不會並查集的神仙戳這兒),還是很好理解的。
$3 克魯斯卡爾算法的例題
•有且只有的一個例題: 洛谷P1546 最短網絡 Agri-Net:
題目背景
農民約翰被選為他們鎮的鎮長!他其中一個競選承諾就是在鎮上建立起互聯網,並連接到所有的農場。當然,他需要你的幫助。
題目描述
約翰已經給他的農場安排了一條高速的網絡線路,他想把這條線路共享給其他農場。為了用最小的消費,他想鋪設最短的光纖去連接所有的農場。
你將得到一份各農場之間連接費用的列表,你必須找出能連接所有農場並所用光纖最短的方案。每兩個農場間的距離不會超過100000
輸入輸出格式
輸入格式:
第一行: 農場的個數,N(3<=N<=100)。
第二行..結尾: 后來的行包含了一個N*N的矩陣,表示每個農場之間的距離。理論上,他們是N行,每行由N個用空格分隔的數組成,實際上,他們限制在80個字符,因此,某些行會緊接着另一些行。當然,對角線將會是0,因為不會有線路從第i個農場到它本身。
輸出格式:
只有一個輸出,其中包含連接到每個農場的光纖的最小長度。
輸入輸出樣例
4
0 4 9 21
4 0 8 17
9 8 0 16
21 17 16 0
28
接下來是我懶得講的代碼(和上面一樣)
1 #include<cstdio> 2 #include<iostream> 3 #include<algorithm> 4 using namespace std; 5 struct point 6 { 7 int x; 8 int y; 9 int v; 10 }; 11 point a[9901]; 12 int fat[101]; 13 int n,i,j,x,m,tot,k; 14 int father(int x) 15 { 16 if(fat[x]!=x) fat[x]=father(fat[x]); 17 return fat[x]; 18 } 19 20 void unionn(int x,int y) 21 { 22 int fa=father(x); 23 int fb=father(y); 24 if(fa!=fb) fat[fa]=fb; 25 } 26 27 int cmp(const point &a,const point &b) 28 { 29 if(a.v<b.v) return 1; 30 else return 0; 31 } 32 33 int main() 34 { 35 cin>>n; 36 for(int i=1;i<=n;++i) 37 for(int j=1;j<=n;++j) 38 { 39 cin>>x; 40 if(x!=0) 41 { 42 m++; 43 a[m].x=i;a[m].y=j;a[m].v=x; 44 } 45 } 46 for(int i=1;i<=n;++i) fat[i]=i; 47 sort(a+1,a+m+1,cmp); 48 for(int i=1;i<=m;++i) 49 { 50 if(father(a[i].x)!=father(a[i].y)) 51 { 52 unionn(a[i].x,a[i].y); 53 tot+=a[i].v; 54 k++; 55 } 56 if(k==n-1) break; 57 } 58 cout<<tot; 59 return 0; 60 }
enddd~~