POJ 1182 食物链 带权并查集

今天一定彻底弄懂 带权并查集

 好吧这题我还是不懂,,,烦躁

现在基本上知道了吧,不过不清楚公式是怎样推出来的,如果要我再写一遍的话,估计会写很多个判断

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <fstream>
 4 #include <algorithm>
 5 #include <cmath>
 6 #include <deque>
 7 #include <vector>
 8 #include <queue>
 9 #include <string>
10 #include <cstring>
11 //#include <unordered_map>
12 #include <map>
13 #include <stack>
14 #include <set>
15 #define LL long long
16 #define INF 0x3f3f3f3f
17 #define OPEN_FILE
18 #define MAXN 50005
19 using namespace std;
20 int n, k;
21 int father[MAXN];
22 int ranK[MAXN];
23 //rank[x]表示x 与 father[x] 的关系,0 是同类,1 是 x 吃 father[x], 2 是 father[x] 吃 x
24 int find(int x){
25     if (x == father[x]) return x;
26     int y = father[x];
27     father[x] = find(father[x]);
28     ranK[x] = (ranK[x] + ranK[y]) % 3;
29     //rank[x]=1,rank[y]=1,表示x吃路径压缩前的的father[x]
30     //压缩前的father[x]吃压缩后的father[x],那么就是压缩后的father[x]吃x
31     //所以rank[x]更新为2
32     //rank[x] = 1, rank[y] = 1 ====> rank[x] = 2
33     //rank[x] = 1, rank[y] = 2 ====> rank[x] = 0
34     //rank[x] = 2, rank[y] = 1 ====> rank[x] = 0
35     //rank[x] = 0, rank[y] = 1 ====> rank[x] = 1
36     //rank[x] = 1, rank[y] = 0 ====> rank[x] = 1
37     //rank[x] = 2, rank[x] = 2 ====> rank[x] = 1
38     return father[x];
39 }
40 void union_set(int x, int y, int z){
41     int a = father[x], b = father[y];
42     father[a] = b;
43     ranK[a] = (z + ranK[y] - ranK[x] + 3) % 3;
44     //这里仅仅是更新了father[x]的rank值, 并没有更新其他的子节点
45     //因为每次查询时都调用了find函数,在路径压缩的回溯过程中就会将其他节点的rank值更新
46     //如果公式看不过来的话,那就有下面这几种情况
47     //rank[x] = 1, rank[y] = 1, z = 1 ====> rank[a] = 1
48     //rank[x] = 0, rank[y] = 1, z = 1 ====> rank[a] = 2
49     //因为情况比较多,就不一一列举了,但是很好奇这个公式是怎样想出来的
50     //看到别人的博客中提到向量?
51 }
52 int main()
53 {
54 #ifdef OPEN_FILE
55     freopen("in.txt", "r", stdin);
56     //freopen("out.txt", "w", stdout);
57 #endif // OPEN_FILE
58     scanf("%d%d", &n, &k);
59     for (int i = 1; i <= n; i++){
60         father[i] = i;
61     }
62     memset(ranK, 0, sizeof(ranK));
63     int x, y, z;
64     int cnt = 0;
65     for (int i = 1; i <= k; i++){
66         scanf("%d%d%d", &z, &x, &y);
67         if (x > n || y > n){
68             cnt++;
69             continue;
70         }
71         if (x == y && z == 2){
72             cnt++;
73             continue;
74         }
75         if (x == y) continue;
76         int a = find(x), b = find(y);
77         if (a == b && ranK[x] != (z - 1 + ranK[y]) % 3){
78             //如果father[x]!=father[y],那这两个还不属于同一个集合,谈不上假话了
79             //属于同一个集合,就有下面几种情况可以保证是真话
80             //z - 1 = 1, rank[y] = 2, rank[x] = 0 给的条件是x吃y,而y被他们的祖先吃,那只有x和father[y]是同类才可能是真话了
81             //z - 1 = 1, rank[y] = 0, rank[x] = 1
82             //z - 1 = 1, rank[y] = 1, rank[x] = 2
83             //z - 1 = 0, rank[y] = 0, rank[x] = 0
84             //z - 1 = 0, rank[y] = 1, rank[x] = 1
85             //z - 1 = 0, rank[y] = 2, rank[x] = 2
86             cnt++;
87         }
88         if (a != b){
89             //还不属于同一个集合的话就肯定是真话,那就把他们的关系加到集合中来
90             union_set(x, y, z - 1);
91         }
92     }
93     printf("%d\n", cnt);
94 }

 

posted on 2015-08-01 17:21  张济  阅读(174)  评论(0编辑  收藏  举报

导航