每日算法——並查集的應用


  並查集是一種群眾喜聞樂見的數據結構,其復雜度是數據結構中最奇葩的之一了,Tarjan證明其為阿克曼函數的反函數,在可以想象(不全面的解釋啊)的范圍內小於等於3。。。我們就把它當做O(1)吧。下面通過幾道基礎的並查集來探查一下並查集的應用。遞歸調用並查集

  最裸的並查集就只有表示一個集合的功能,支持動態的合並,查詢兩者是否屬於一個集合,這部分內容太簡單,請自行Baidu。

  在並查集上可以加入邊權,成為加權並查集,一般來說這類題目形式比較單一,純屬個人娛樂吧。。

  加權並查集裸題——銀河英雄傳說

  這是一道最裸的加權並查集,對於每一個節點維護一個到最老最先的邊權值和,再在祖先上維護一個Size,表示集合的大小。Wikioi上的題解有個水表說不能路徑壓縮。。。怎么可能嘛!!!我們仔細思考一下,每一個點在一個集合中只會被壓縮一次,壓縮之后就會被連接到祖先上,到祖先的權值和等於到原來的父親的權值加上原來的父親到祖先的權值和,只需要在壓縮的時候更新一下就好了。合並的時候,把兩個集合(不妨設A,B)合並在一起,假如把A並入B,則把A的祖先節點連在B的祖先上,邊權設為B的Size。相當於把A集合直接接在B的后面,如此,這個問題就被很好的解決了。

  代碼:

 1 #include <iostream>
2 #include <cstdio>
3 #include <cstdlib>
4 #include <algorithm>
5
6 using namespace std;
7 struct spaceship
8 {
9 int father;
10 int len,maxlen;
11
12 spaceship(){len=0;maxlen=1;}
13 }sp[30010];
14
15 int get(int n)
16 {
17 if (sp[n].father==n) return n;
18 int m=get(sp[n].father);
19 sp[n].len+=sp[sp[n].father].len;
20 sp[n].father=m;
21 return sp[n].father;
22 }
23
24 void marry(int n,int m)
25 {
26 int nn=get(n);
27 int mm=get(m);
28 sp[nn].father=mm;
29 sp[nn].len+=sp[mm].maxlen;
30 sp[mm].maxlen+=sp[nn].maxlen;
31 }
32
33 int main()
34 {
35 int n;
36 scanf("%d",&n);
37 for (int i=1;i<=30000;i++) sp[i].father=i;
38 for (int i=1;i<=n;i++)
39 {
40 char ch;
41 int j,k;
42 char s[5];
43 scanf("%s",s);
44 if (s[0]=='M')
45 {
46 scanf("%d%d",&j,&k);
47 marry(j,k);
48 }
49 else
50 {
51 scanf("%d%d",&j,&k);
52 if (get(j)==get(k))
53 {
54 printf("%d\n",abs(sp[j].len-sp[k].len)-1);
55 }
56 else
57 printf("-1\n");
58 }
59 }
60 return 0;
61 }
View Code

  上面這道是裸題,下面有一個看起來不裸的的水題:

  加權並查集弱題——食物鏈

  初一看這道題似乎和並查集關系不大,但是仔細觀察可以發現,不同的個體存在一些關系。我們將物種設為0,1,2三種,則給出的條件分為兩種——1.兩個個體數字(物種)相同。2,個體A數字=(個體B數字+1)%3 。是否有了一點發現呢,如果是相同的數字,並且不屬於同一個集合,就想合並辦法讓他們之間的權值變為0(mod 3),同理,可以讓其數字變為1或2(mod 3),只需要在路徑壓縮的時候取模就行了。水題解決,代碼用下一道的吧。。。

  原創題——魏總數星星

  

魏總數星星

star.cpp/c/pas

【描述】

魏總,也就是DP魏,非常喜歡星星,有一天他躺在草坪上數星星。天上共有i顆星星,魏總把天空分成了K個扇形,繞着天空的中心——月亮排布。月亮看見魏總喜歡星星,非常不爽,她就想考一下魏總。月亮給出n隊星星的相互關系,形如a b p表示b星星在a星星所在扇形區域的順時針方向第p個扇形內(0<=p<=k)(p==0時表示在同一個扇形內)。最后月亮要詢問m次,形如a b表示詢問a b兩星是否在一個扇形內,是則輸出“Yes”,不是則輸出“No”,不知道則輸出“Unknown”。由於月亮看魏總喜歡星星變得心情急躁,可能有一些關系與前面的關系矛盾,則這些關系無效。月亮說如果不能把她的所有詢問答對就要發出強光,讓魏總看不到星星,而本來是大神的魏總因為想見到星星不能編程,只有把這個艱巨的任務交給你了。

【輸入】

第一行四個整數iknm表示i個星星,k個扇形,n個關系,m個詢問。

接下來n行,每行三個整數a b p 表示表示b星星在a星星所在扇形區域的順時針方向第p個扇形內。

接下來m行,每行兩個整數a b表示詢問ab是否在同一個扇形內。

【輸出】

m行,每行為“Yes”或“No”或“Unknown”對應每一個詢問

【樣例輸入】

5 5 3 3

1 2 1

2 4 2

4 5 2

1 2

3 4

1 5

【樣例輸出】

No

Unknown

Yes

【數據范圍】

20%,魏總數不超過100個星星,月亮詢問不超過100次,天空被分成不超過10個區域。

50%,魏總數不超過4000個星星,月亮詢問不超過4000次,天空被分成不超過1000個區域。

100%,魏總數不超過100000個星星,月亮詢問不超過100000次,天空被分成不超過10000個區域,關系數少於200000

 

其實很容易發現這道題和食物鏈是一模一樣的,只不過把邊權值改為了比較大的數,只需稍微改一下代碼即可,代碼如下:

 

 1 #include <iostream>
2 #include <cstdio>
3 #include <cstdlib>
4 #include <algorithm>
5
6 using namespace std;
7 struct star
8 {
9 int father;
10 int len;
11
12 star()
13 {
14 father=0;
15 len=0;
16 };
17 }st[100010];
18 int mod;
19 int get(int now)
20 {
21 if (st[now].father==now)
22 {
23 return now;
24 }
25 int f=get(st[now].father);
26 st[now].len=(st[now].len+st[st[now].father].len)%mod;
27 if (st[now].len==0) st[now].len=mod;
28 st[now].father=f;
29 return f;
30 }
31 void marry(int a,int b,int l)
32 {
33 int bf=get(b);
34 int af=get(a);
35 int len=st[a].len+l;
36 len=len % mod;
37 if (l>=st[b].len)
38 {
39 st[bf].father=a;
40 st[bf].len=l-st[b].len;
41 }
42 else
43 {
44 st[bf].father=a;
45 st[bf].len=mod+l-st[b].len;
46 }
47 }
48
49 int main()
50 {
51 freopen ("star.in","r",stdin);
52 freopen ("star.out","w",stdout);
53 int n,m,k;
54 scanf("%d%d%d%d",&n,&mod,&m,&k);
55 for (int i=1;i<=n;i++)
56 {
57 st[i].father=i;
58 }
59 int a,b,p;
60 for (int i=1;i<=m;i++)
61 {
62 scanf("%d%d%d",&a,&b,&p);
63 if (get(a)!=get(b))
64 {
65 marry(a,b,p);
66 }
67 }
68 for (int i=1;i<=k;i++)
69 {
70 scanf("%d%d",&a,&b);
71 if (get(a)!=get(b))
72 {
73 printf("Unknown\n");
74 }
75 else
76 {
77 if (st[a].len%mod==st[b].len%mod) printf("Yes\n");
78 else printf("No\n");
79 }
80 }
81 return 0;
82 }
View Code

 

總結:並查集可以處理沒有分離操作的一類問題,可以在極快的時間內處理元素間相互關系的問題,好寫好用,就是沒什么地方用。。。

 


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com