並查集_貪心_求無向圖最短連通路徑_最小生成樹(kruskal)


 

A: 樹學家丁丁妹

題目描述

為了響應國家“退耕還林”的號召,丁丁妹正在將她的大頭菜田改造成樹林。

然而這和這道題並沒有什么關系。

重要的是,丁丁妹思考了如下一個問題:

給定一個有n 個點m 條邊的無向圖,每條邊有一個邊權c 。

 

如何選擇n−1 條邊來讓這個無向圖連通,並且使得這n−1條邊的邊權之和最小呢?

顯然這個問題對於丁丁妹來說太困難了,於是她又花重金聘請了你,希望你來解決這個問題。 

輸入描述

單組數據,第一行為兩個正整數n,m 。

接下來m 行,每行有三個正整數x,y,c ,表示x 號點和y號點之間存在一條邊權為c 的無向邊。

 

數據保證:

1. 對於80% 的數據, 1 ≤ n,m ≤ 1000

2. 對於100%  的數據,1≤n,m≤1000000

3. 對於100%  的數據, 1 ≤ c ≤ 100

輸出描述

 

一個整數 c ,代表邊權之和的最小值;若無法選擇n−1條邊讓圖連通,輸出− 1 。

樣例輸入

3 3

1 2 1

1 3 2

2 3 3

樣例輸出

3

 

 

思路:

這個題一看數據量 1e6 這么大,指定不能用二維數組,所以最短路或者dp直接求實在是行不通,

問的是聯通圖, 最短連通路徑,又需要壓縮空間來優化, 很容易就想到並查集

另外, 注意這里說的連通路徑不是那種"一筆畫的"歐拉路, 而是 ------- 連通的可交叉的路徑-------類似一棵樹

實際上---------最小生成樹------------就是我們要求的連通路徑

問題在於怎么使用並查集來表示一條完整的,"從1-n都能連通的路徑",另外每條路徑都該怎么算出來

 

並查集來表示存在的連通關系, 我們知道 find()函數就是為了 將所有連通的節點歸到同一個"根"上面,

這樣可以形成一個"同根樹",如果發現所有的節點都只有一個根, 說明這個圖是聯通的,

最小生成樹的求解-----Kruskal算法/Prim算法-----實際上就是貪心!!! 

這里用Kruskal , 我們把所有的邊排序,每次操作,

  • 檢查是否加入新邊,是否和已有邊連通沖撞
  • 是則加入, 並且更新節點的根(合並)

最后只檢查新邊是否和成環,最后檢查是否所有的點都連通,檢查邊的數目是否為n-1即可

注意合並是怎樣的,比如

      6         和       4                  

1    3   5           2     7

 存在5,2 之間有一條邊, 顯然不是5-2合並,因為這樣就會有兩個根了, 檢測連通靠的是根是否相同,

所以, 另一棵樹的根,   也就是6  ,接到另一棵樹,

至於怎么接,    這里看需要, 如果保持結構的話, 就要以5為根旋轉成, 

  5              類似於平衡樹, 6的爸爸變成5,   然后 5 的爸爸變成2,這樣就保持了原來的結構性質

  6

1   3

如果需要徹底壓縮,盡量優化查找時間,那就讓, 6, 1, 3, 5 的爸爸變成 4

假如不要求時間和效率, 我們只要求聯通路徑長, 簡單合並就可以了 

----比如, 6---右邊的爸爸-------變成--------左邊的爸爸-------2也可以得到確結果,如下代碼,  但是會TE

 

1 int l=find(x[i].l);
2 int r=find(x[i].r);
3 f(r!=l) {
4     k++;
5     ans+=x[i].d;
6     f[r]=x[i].l;
7 }

 

改成了把6的爸爸變成4, 還是TE,6---右邊的爸爸-------變成--------左邊的爸爸-------2

1 if(r!=l) {
2     k++;
3     ans+=x[i].d;
4     f[r]=l;
5 }

 

原因在於沒有固定好一個策略合並, "右邊的合並到左邊的"並不是一個有序的策略,

因為節點是有序號的, 但是, 輸入的時候並沒有規定大的節點一定在右邊

也不能保證, 比如大的節點合並到小的那里去,

所以加個判斷條件,改成這樣就過了

1 if(r!=l) {
2     k++;
3     ans+=x[i].d;
4     if(r>l)f[r]=l;
5     else f[l]=r;
6 }

完整代碼:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 using namespace std;
 6 typedef long long ll;
 7 const int M=1000000+10;
 8 int n,m;
 9 struct D {
10     int l,r;
11     int d;
12     D() {
13         l=0,r=0,d=0;
14     }
15 } x[M];
16 int f[M];
17 void init() {
18     for(int i=1; i<=n; i++) {
19         f[i]=i;
20     }
21 }
22 int find(int t) {
23     return f[t]==t?t:find(f[t]);
24 }
25 /*
26 int find(int t1) {
27     int t=t1;
28     while(f[t]!=t) {
29         t=f[t];
30     }
31     return t;
32 }
33 */
34 bool cmp(D a,D b) {
35     return a.d<b.d;
36 }
37 int main () {
38     memset(x,0,sizeof(x));
39     scanf("%d%d",&n,&m);
40     init();
41     ll ans=0;
42     for(int i=1; i<=m; i++) {
43         int l,r;
44         scanf("%d%d%d",&x[i].l,&x[i].r,&x[i].d);
45     }
46     sort(x+1,x+1+m,cmp);
47     int k=0;
48     bool t=0;
49     for(int i=1; i<=m; i++) {
50         int l=find(x[i].l);
51         int    r=find(x[i].r);
52         if(r!=l) {
53             k++;
54             ans+=x[i].d;
55             if(r>l)f[r]=l;
56             else f[l]=r;
57         }
58         if(k==n-1) {
59             t=1;
60             break;
61         }
62     }
63     if(t==0)cout<<"-1";
64     else printf("%lld",ans);
65     cout<<"\n";
66     return 0;
67 }
View Code

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


免責聲明!

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



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