食物鏈(並查集經典題)


食物鏈

時間限制:1000 ms | 內存限制:65535 KB  

poj-1182

描述
動物王國中有三類動物A,B,C,這三類動物的食物鏈構成了有趣的環形。A吃B, B吃C,C吃A。
現有N個動物,以1-N編號。每個動物都是A,B,C中的一種,但是我們並不知道它到底是哪一種。
有人用兩種說法對這N個動物所構成的食物鏈關系進行描述:
第一種說法是"1 X Y",表示X和Y是同類。
第二種說法是"2 X Y",表示X吃Y。
此人對N個動物,用上述兩種說法,一句接一句地說出K句話,這K句話有的是真的,有的是假的。當一句話滿足下列三條之一時,這句話就是假話,否則就是真話。
1) 當前的話與前面的某些真的話沖突,就是假話;
2) 當前的話中X或Y比N大,就是假話;
3) 當前的話表示X吃X,就是假話。
你的任務是根據給定的N(1 <= N <= 50,000)和K句話(0 <= K <= 100,000),輸出假話的總數。

輸入
第一行是兩個整數N和K,以一個空格分隔。
以下K行每行是三個正整數 D,X,Y,兩數之間用一個空格隔開,其中D表示說法的種類。
若D=1,則表示X和Y是同類。
若D=2,則表示X吃Y。
輸出
只有一個整數,表示假話的數目。
樣例輸入
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
樣例輸出
3

 

這道題很經典,第一次做的帶權的並查集,加深了我對並查集的理解與應用,去網上看的代碼,有篇博客非常簡潔,解題關鍵代碼就只有7.8行的樣子吧。沒有很詳細的注釋,就這7,8行,看了我足足6.7小時,

收獲挺大,我想把這題關鍵的部分寫下來,以后可以看看,順便也許能幫到人吧。

這是我參考的一篇博客:很詳細,但是說得有點太多了,我都看得累了,代碼也有點多了。。。下面的代碼才是我說的只有7行關鍵的簡潔代碼,我寫的類似與那篇博客

http://blog.csdn.net/c0de4fun/article/details/7318642

這道題的代碼比較精巧,先說總體思路

剛拿到這題,我想的是能不能將之化為3個並查集ABC,然后每個並查集頭結點都記錄着這類動物是什么屬性,然后發現,根本行不通,因為不好確定到底是哪種屬性的動物

思路:這個的思路是將所有說到的動物都放在一個並查集,就是說只要有關系,就連在一起。

1.find_head()找了頭結點還壓縮了路徑到只有兩層,這樣就很容易判斷兩者的關系了,壓縮路徑時,就要適當的改變關系了

2.如果是同一個頭結點的話,對頭結點的關系要推個式子,下面就說

3.如果不在一個頭結點,就說明沒有關系嘛。肯定是真話,然后直接連起來就行了。連起來要又需要推導個式子,難住我很久。。。

 

f[N]是存 N 號動物的父節點的編號,r[N]是對父節點的關系 0 同類,1 被父節點吃,2 吃父節點 

find_head()函數遞歸不但找了頭結點,還將搜索路徑上的節點都壓縮路徑,將它直接連接到了頭結點,對遞歸熟悉的不難理解

關鍵在於確定對 爺爺節點 的那個公式上,窮舉一下,然后就能很輕松的看出來的,關系有點像個三角形

 r[f[x]]   r[x]

父親對爺爺  兒子對父親  兒子對爺爺 

   0      0       0      所以,兒子對爺爺就可以推出這個式子 (r[x] + r[f[x]] )%3  

   0      1        1   

   0      2       2   

   1      0       1  

   1      1       2  

   1      2       0  

   2      0       2  

   2      1       0     

   2      2       

圖:

用這個式子就解決了壓縮路徑的關系轉換

 

然后到了將並查集聯合這個函數,也就是 3 的情況,這是最難的部分:

前面說了,用find_head()找頭結點,並且直接連上頭結點,然后判斷兩個的頭結點是否相等,

  1,相等就判斷關系是否矛盾,因為只有兩層,枚舉一下,容易發現規律,如果 (r[y]-r[x]+3)%3!=d-1  代表假話

或者你足夠理解那個find_head()的那個公式 3-r[x] 是 fx 對 x 的關系與 r[y] 相加一下就是 y 對 x 的關系看是否等於d-1。

  2,不同頭結點就有點麻煩,像個四邊形的關系,所以化成兩個三角形。

    3-r[y]是 y 的頭結點對 y 的關系,這個好理解吧,

    d-1是 y 對 x 的關系,因為 d 原本是 x 對 y 的關系,減 1 正好反過來。

然后用上我們前面的find里面的那個對父節點的公式, (r[x] +r[f[x]] )%3  ,帶進去,就得到 fy 對 x 的關系了,

然后再加上 x 對 fx 的關系就得到了fy 對 fx 的關系, 這不就分成了2個三角形,得到了 r[fy] = 什么。,

不懂,看圖:已知標出,要求綠色,先求斜的,再求 r[fy]

 

寫完博客,發現更加理解這題了,也更加理解怎么推導公式啥的了,有收獲。

 1 #include <stdio.h>
 2 
 3 int const N=50005;  4 int f[N];  5 int r[N];  6 
 7 int find_head(int x)  8 {  9     int fx=x; 10     if (x!=f[x]) 11  { 12         fx=find_head(f[x]); 13         r[x]=(r[x]+r[f[x]])%3;     //這是窮舉可以輕松看出來的公式,壓縮路徑
14         f[x]=fx; 15  } 16     return f[x]; 17 } 18 
19 int Union(int d,int x,int y) 20 { 21 
22     int fx=find_head(x); 23     int fy=find_head(y); 24     if (fx!=fy) 25  { 26         f[fy]=fx; 27         r[fy]=(3-r[y]+d-1+r[x])%3;      //很重要的一個公式,由find里面的公式推導過來
28         return 0; 29  } 30     if ((r[y]-r[x]+3)%3!=d-1) return 1; 31     return 0; 32 } 33 
34 int main() 35 { 36     int n,k; 37     int d,x,y; 38     int ans=0; 39     scanf("%d%d",&n,&k); 40     for (int i=1;i<=n;i++) 41  { 42         f[i]=i; 43         r[i]=0; 44  } 45     while (k--) 46  { 47         scanf("%d%d%d",&d,&x,&y); 48         if (x>n||y>n||(x==y&&d==2)) 49  { 50             ans++; 51             continue; 52  } 53         if (Union(d,x,y)) ans++; 54  } 55     printf("%d\n",ans); 56     return 0; 57 }
View Code

 


免責聲明!

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



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