1. 程式人生 > >【並查集】模板 + 【HDU 1213、HDU 1232、POJ 2236、POJ 1703】例題詳解

【並查集】模板 + 【HDU 1213、HDU 1232、POJ 2236、POJ 1703】例題詳解

不想看模板,想直接看題目的請戳下面目錄:

目錄:

HDU 1213 How Many Tables【傳送門】

HDU 1232 暢通工程 【傳送門】

POJ 2236 Wireless Network 【傳送門】

POJ 1703 Find them, Catch them 【傳送門】


先上模板:

 1 #define MAXN 根據編號需要
 2 int per[MAXN],rank[MAXN];
 3 
 4 void init(int n)
 5 {
 6     int i;
 7     for(i=1;i<=n;i++)
 8     {
 9         per[i]=i;rank[i]=0
10     }
11         
12 }
13 
14 int find(int x)
15 {
16     if(x==per[x])
17         return x;
18     return per[x]=find(per[x]);
19 }
20 
21 void unite(int x,int y)
22 {
23     x=find(x);
24     y=find(y);
25     if(x==y)
26         return;
27     if(rank[x]>rank[y])
28         per[y]=x;
29     else
30     {
31         per[x]=y;
32         if(rank[x]==rank[y])
33             rank[y]+=1;
34     }     
35 }
36 
37 bool same(int x,int y)
38 {
39     return find(x)==find(y);
40 }
View Code

 

並查集詳解請訪問:(通俗易懂解釋)

https://blog.csdn.net/lesileqin/article/details/96703143


接下來來看幾個例子:

HDU 1213 How Many Tables【傳送門】

題目大意:有一個人過生日,請到了他的諸多朋友,但是這些朋友之間有的認識,有的不認識。這個人想盡可能的把相互之間認識的人湊到一張桌子上,不認識的人則去另一張桌子。朋友們互相認識的規則是:比如A認識B,B認識C,那麼A,B,C就可以湊到一桌子上。現在問:他的朋友們以這樣的規則能湊夠幾桌。

解題思路:運用並查集,把相互認識的人都聯合一下,到最後計算陣列中有幾個per值等於自身的人就可以了。

 1 #include<iostream>
 2 
 3 using namespace std;
 4 
 5 int f[1005];
 6 
 7 void init(int n)
 8 {
 9     for(int i=1;i<=n;i++)
10         f[i]=i;
11 }
12 
13 int find(int a)
14 {
15     while(a!=f[a])
16     {
17         a=f[a];
18     }
19     return a;
20 }
21 
22 void Combin(int a,int b)
23 {
24     int ta,tb;
25     ta=find(a);
26     tb=find(b);
27     
28     if(ta!=tb)
29         f[ta]=tb;
30 }
31 
32 int answer(int n)
33 {
34     int sum=0;
35     for(int i=1;i<=n;i++)
36         if(f[i]==i)
37             sum++;
38         return sum;
39 }
40 
41 int main()
42 {
43     
44     int t;
45     cin >> t;
46     while(t--)
47     {
48         int n,m;
49         cin >> n >> m;
50         init(n);
51         int a,b;
52         for(int i=0;i<m;i++)
53             cin >> a >> b,Combin(a,b);
54         cout << answer(n) << endl ;
55     }
56     
57     return 0;
58 }
View Code

HDU 1232 暢通工程 【傳送門】

此處略去題目大意……

解題思路:其實這個題目和上一個題異曲同工,就是變換了一下條件,我們假設村子是上題的朋友,那麼路就是朋友們之間的關係,那麼兩堆朋友之間連線,其實只需要一次就可以了,同樣,三堆朋友之間要想相互認識,只需要兩個關係就可以了,所以我們很容易可以得出朋友們要想都認識,只需要把朋友堆數 - 1 就可以了。

程式碼只需要將上題的輸出結果-1即可 ,不再貼出。


POJ 2236 Wireless Network 【傳送門】

題目大意:一片廢棄的地方,ACM協會正在進行救援,可是所有電腦的通訊設施都被破壞了,被修復好的電腦的訊號傳輸距離只有d米,現在給出所有電腦的座標,然後給出指令S與O,S後跟兩個數字代表兩個電腦的編號,詢問這兩臺電腦是否能通訊,如果能則輸出SUCCESS,否則輸出FALL;O後跟一個編號,代表修好一定電腦。

解題思路:此題運用並查集,在修復好一臺電腦之後遍歷所有修好的電腦,如果兩點間距離小於d米,則連通兩個點;遇到S的時候只需要判斷是否連通即可。

  1 #include<iostream>
  2 #include<cmath>
  3 using namespace std;
  4 #define MAXN 1005
  5 
  6 //座標 
  7 struct pos{
  8     int x,y;
  9     int par_id;        //父輩id 
 10     int rank;            //樹的高度 
 11 }; 
 12 
 13 struct pos par[MAXN];        //記錄父親 
 14 bool isok[MAXN];    //是否被修復成功 
 15 
 16 float Distance(struct pos a,struct pos b)
 17 {
 18     float dis=sqrt(fabs(a.x-b.x)*fabs(a.x-b.x)+fabs(a.y-b.y)*fabs(a.y-b.y));
 19     return dis;
 20 }
 21 
 22 void init(int n)
 23 {
 24     for(int i=1;i<=n;i++)
 25     {
 26         par[i].par_id=i;
 27         par[i].rank=0;
 28         isok[i]=false;    
 29     }
 30 }
 31 
 32 //查樹根 
 33 int find(int x)
 34 {
 35     if(par[x].par_id==x)
 36         return x;
 37     else
 38         return par[x].par_id=find(par[x].par_id);
 39 }
 40 
 41 //合併 
 42 void unite(int x,int y)
 43 {
 44     x=find(x);
 45     y=find(y);
 46     if(x==y)
 47         return;
 48     if(par[x].rank<par[y].rank)
 49         par[x].par_id=y;
 50     else{
 51         par[y].par_id=x;
 52         if(par[x].rank==par[y].rank)
 53             par[x].rank++;
 54     }
 55 }
 56 
 57 //判斷x和y是不是同一個集合
 58 bool same(int x,int y)
 59 {
 60     return find(x)==find(y);
 61 } 
 62 
 63 int main()
 64 {
 65     int N,d;
 66     char c;
 67     cin >> N >> d;
 68     init(N);
 69     for(int i=1;i<=N;i++)
 70     {
 71         int x,y;
 72         cin >> x >> y; 
 73         par[i].x=x;
 74         par[i].y=y;
 75     }
 76     
 77     while(cin >> c)
 78     {
 79         int p,q;
 80         if(c=='O')
 81         {
 82             cin >> p;
 83             isok[p]=true;
 84             for(int i=1;i<=N;i++)
 85             {
 86                 if(isok[i]==true&&i!=p)
 87                 {
 88                     struct pos a,b;
 89                     a.x=par[i].x;
 90                     a.y=par[i].y;
 91                     a.par_id=par[i].par_id;
 92                     b.x=par[p].x;
 93                     b.y=par[p].y;
 94                     b.par_id=par[p].par_id;
 95                     if(Distance(a,b)<=d)
 96                     {
 97                     //    cout << Distance(a,b) << endl;
 98                         unite(p,i);
 99                     }
100                         
101                 }
102             }
103         }
104         else if(c=='S')
105         {
106             cin >> p >> q;
107             if(same(p,q))
108                 cout << "SUCCESS\n";
109             else
110                 cout << "FAIL\n";
111         }
112 //        for(int i=1;i<=N;i++)
113 //            cout << par[i].par_id << " ";
114 //        cout << endl;
115     }
116     return 0;
117 }
View Code

POJ 1703 Find them, Catch them 【傳送門】

題目大意:有兩個幫派,警察現在抓住了N個罪犯,輸入字母A或D,後面跟著兩個罪犯的編號。如果字元是A,則代表這一次詢問:如果不是一個幫派,則輸出“In different gangs.”;是一個幫派輸出"In the same gang.";否則輸出“Not sure yet.”。如果是字元D,那就代表著後面的兩個編號不屬於一個幫派!

解題思路:需要一個標記陣列vis,用來標記兩個罪犯(a,b)不屬於同一個幫派,如果標記陣列兩個罪犯都為0則代表不確定是哪個幫派,並且讓vis[a]=b,vis[b]=a;如果vis[a]==0&&vis[b]!=0,則讓vis[b]=a,並且連通a,vis[b];如果vis[b]==0&&vis[a]!=0,則讓vis[a]=b,並且連通b,vis[a];最後一種情況是a與b的標記陣列都不為0,那麼連2019-07-21通a,vis[b],連通b,vis[a]最後判斷即可。

  1 //#include<iostream>
  2 //using namespace std;
  3 #include<stdio.h>
  4 #define MAXN 100010
  5 
  6 int per[MAXN],rank[MAXN],vis[MAXN];
  7 
  8 void init(int n)
  9 {
 10     int i;
 11     for(i=1;i<=n;i++)
 12     {
 13         per[i]=i;rank[i]=0;vis[i]=0;
 14     }
 15         
 16 }
 17 
 18 int find(int x)
 19 {
 20     if(x==per[x])
 21         return x;
 22     return per[x]=find(per[x]);
 23 }
 24 
 25 void unite(int x,int y)
 26 {
 27     x=find(x);
 28     y=find(y);
 29     if(x==y)
 30         return;
 31     if(rank[x]>rank[y])
 32         per[y]=x;
 33     else
 34     {
 35         per[x]=y;
 36         if(rank[x]==rank[y])
 37             rank[y]+=1;
 38     }     
 39 }
 40 
 41 int Same(int x,int y)
 42 {
 43     if(find(x)==find(y))
 44         return 1;
 45     
 46 }
 47 
 48 int main()
 49 {
 50 //    ios::sync_with_stdio(false);
 51     int t;
 52     scanf("%d",&t);
 53 //    cin >> t;
 54     while(t--)
 55     {
 56         int n,m;
 57         scanf("%d%d",&n,&m);
 58 //        cin >> n >> m;
 59         init(n);
 60         while(m--)
 61         {
 62             int a,b;
 63             char c[2];
 64             scanf("%s",c);
 65             //getchar();
 66             scanf("%d%d",&a,&b);
 67             //cin >> c >> a >> b;
 68             //聯立 
 69             //printf("%c\n",c);
 70             if(c[0]=='D')
 71             {
 72                 if(vis[a]==0&&vis[b]==0)
 73                 {
 74                     vis[a]=b;
 75                     vis[b]=a;
 76                 }
 77                 else if(vis[a]==0)
 78                 {
 79                     vis[a]=b;
 80                     unite(a,vis[b]);
 81                 }
 82                 else if(vis[b]==0)
 83                 {
 84                     vis[b]=a;
 85                     unite(b,vis[a]);
 86                 }
 87                 else{
 88                     unite(a,vis[b]);
 89                     unite(b,vis[a]);
 90                 }
 91             }
 92             else{
 93                 if(Same(a,b))
 94                     printf("In the same gang.\n");
 95                     //cout << "In the same gang.\n";
 96                 else if(Same(a,vis[b]))
 97                     //cout << "In different gangs.\n";
 98                     printf("In different gangs.\n");
 99                 else
100                     printf("Not sure yet.\n");
101                     //cout << "Not sure yet.\n";
102             }
103         }
104     }
105     
106     return 0;
107 }
View Code

&n