1. 程式人生 > >poj2492 A Bug's Life(帶權並查集)

poj2492 A Bug's Life(帶權並查集)

tab set 復雜 鏈接 image 之間 判斷 clu 成了

題目鏈接

http://poj.org/problem?id=2492

題意

蟲子有兩種性別,有n只蟲子,編號1~n,輸入m組數據,每組數據包含a、b兩只蟲子,表示a、b為不同性別的蟲子,根據輸入的m組數據是否出現前後矛盾(如a、b在前面判斷為同性,而後又得出a、b為異性)進行相應的輸出。

思路

使用並查集求解,但該題比普通並查集題目復雜了一些,該題需要使用樹中結點的權值來記錄信息,在代碼中使用數組r[]來記錄某結點和其父結點之間的性別是否相同,若r[x]=0,則說明蟲子x和其父結點同性,若r[x]=1,則說明x與其父結點異性,這也是“帶權”的含義。

代碼

 1
#include <cstdio> 2 3 const int N = 2000 + 10; 4 int p[N]; 5 int r[N]; 6 7 void make_set(int n) 8 { 9 for (int i = 1;i <= n;i++) 10 { 11 p[i] = -1; 12 r[i] = 0; 13 } 14 } 15 16 int find_root(int x) 17 { 18 if (p[x] == -1) return x; 19 20
int t = p[x]; 21 p[x] = find_root(p[x]); 22 r[x] = (r[x] + r[t]) % 2; 23 return p[x]; 24 } 25 26 void union_set(int a, int b) 27 { 28 int ra = find_root(a); 29 int rb = find_root(b); 30 31 p[ra] = rb; 32 r[ra] = (r[a] + r[b] + 1) % 2; 33 } 34 35 int main()
36 { 37 //freopen("poj2492.txt", "r", stdin); 38 int t; 39 scanf("%d", &t); 40 int cnt = 0; 41 while (t--) 42 { 43 int n, m; 44 scanf("%d%d", &n, &m); 45 make_set(n); 46 bool flag = true; 47 int a, b; 48 for (int i = 0; i < m; i++) 49 { 50 scanf("%d%d", &a, &b); 51 if (find_root(a) == find_root(b)) 52 { 53 if (r[a] == r[b]) 54 { 55 flag = false; 56 continue; 57 } 58 } 59 else union_set(a, b); 60 } 61 printf("Scenario #%d:\n", ++cnt); 62 if (flag) 63 puts("No suspicious bugs found!\n"); 64 else puts("Suspicious bugs found!\n"); 65 } 66 return 0; 67 }

分析

第一次做帶權並查集,下面是我對代碼的一些分析:

1、首先要註意函數int find_root(int x)。函數find_root不僅進行了路徑壓縮,而且還更新了結點和其父結點之間的關系,更新後的關系r[x]=(r[x]+r[t])%2,其中t為x的父結點。為什麽是r[x]=(r[x]+r[t])%2呢?首先要知道路徑壓縮後樹變成了2層,每個結點直接與根結點相連,假設有3只蟲子a、b、c,關系如下

技術分享圖片

則更新前有r[a]=1,r[b]=1,更新後r[a]=0=(r[a]+r[b])%2,考慮全部情況,則可以得到下表:

更新前r[a] r[b] 更新後r[a]
0 0 0
0 1 1
1 0 1
1 1 0

可以看到有r[a]=(r[a]+r[b])%2(其實就是把更新前r[a]和r[b]做了一次異或操作得到新的r[a],寫成r[a]=r[a]^r[b]也可以)。

2、還要註意函數union_set中為什麽會有r[ra]=(r[a]+r[b]+1)%2?分析方法和上面是相同的,把r[a]、r[b]、r[ra]的值列舉出來就可以發現r[ra]=(r[a]+r[b]+1)%2

相似題目

1、poj1703

poj2492 A Bug's Life(帶權並查集)