最小生成樹--克魯斯卡爾算法(Kruskal)


按照慣例,接下來是本篇目錄:

$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個農場到它本身。

 

輸出格式:

 

只有一個輸出,其中包含連接到每個農場的光纖的最小長度。

 

輸入輸出樣例

  輸入樣例#1: 復制
  4
  0 4 9 21
  4 0 8 17
  9 8 0 16
  21 17 16 0
  輸出樣例#1: 復制
  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~~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM