[关闭]
@LinKArftc 2015-08-14T11:51:06.000000Z 字数 6847 阅读 1133

并查集

数据结构 并查集


总结

做了一天的并查集,发现并查集其实并没有自己想象的那么简单,子节点与父节点的关系 rela[i] 递推还有需要记录的状态信息还是挺麻烦的,不过做了一天之后,发现还是挺水的,基本都差不多,就看自己能不能找到需要记录的状态信息和关系递推了。

例题

poj 1182

经典的食物链
这题有个大坑,就是不能多组数据读入,调了一上午,最终看discuss才发现这个坑,快哭了都ToT
需要记录子节点与父节点的关系 rela[i], 0表示同类,1 吃,2 被吃,然后就是递推了

  1. /*
  2. ID: LinKArftc
  3. PROG: 1182.cpp
  4. LANG: C++
  5. */
  6. #include <cstdio>
  7. #include <iostream>
  8. #include <cstring>
  9. #include <algorithm>
  10. using namespace std;
  11. const int maxn = 50010;
  12. int n, k;
  13. int rela[maxn];//0 same, 2 eat, 3 eaten;
  14. int father[maxn];
  15. void init() {
  16. for (int i = 1; i <= n; i ++) {
  17. father[i] = i;
  18. rela[i] = 0;
  19. }
  20. }
  21. int find(int a) {
  22. if (father[a] == a) return a;
  23. int fa = father[a];
  24. father[a] = find(father[a]);
  25. rela[a] = (rela[a] + rela[fa] + 3) % 3;
  26. return father[a];
  27. }
  28. void unin(int op, int a, int b) {
  29. int x = find(a);
  30. int y = find(b);
  31. father[y] = x;
  32. rela[y] = (3 + rela[a] - rela[b] - op) % 3;
  33. }
  34. int main() {
  35. scanf("%d %d", &n, &k);
  36. int ans = 0;
  37. int op, a, b;
  38. init();
  39. for (int i = 1; i <= k; i ++) {
  40. scanf("%d %d %d", &op, &a, &b);
  41. if (a > n || b > n) ans ++;
  42. else if (a == b && op == 2) ans ++;
  43. else {
  44. int x = find(a);
  45. int y = find(b);
  46. if (x == y) { // relation sure
  47. if (op == 1 && rela[a] != rela[b]) ans ++;
  48. if (op == 2 && (rela[a] - rela[b] + 3) % 3 != 1) ans ++;
  49. } else unin(op - 1, a, b);
  50. }
  51. }
  52. printf("%d\n", ans);
  53. return 0;
  54. }

poj 1703 帮派

跟上一题差不多

  1. /*
  2. ID: LinKArftc
  3. PROG: 1703.cpp
  4. LANG: C++
  5. */
  6. #include <cstdio>
  7. #include <iostream>
  8. #include <cstring>
  9. #include <algorithm>
  10. using namespace std;
  11. const int maxn = 100010;
  12. int father[maxn];
  13. int rela[maxn];//1 same, 0 dif;
  14. int n, m;
  15. void init() {
  16. for (int i = 1; i <= n; i ++) {
  17. father[i] = i;
  18. rela[i] = 1;
  19. }
  20. }
  21. int find(int a) {
  22. int x = father[a];
  23. if (x != a) {
  24. father[a] = find(x);
  25. if (rela[a] == rela[x]) rela[a] = 1;
  26. else rela[a] = 0;
  27. }
  28. return father[a];
  29. }
  30. void unin(int a, int b) {
  31. int x = find(a);
  32. int y = find(b);
  33. if (x != y) {
  34. father[x] = y;
  35. if (rela[a] == rela[b]) rela[x] = 0;
  36. else rela[x] = 1;
  37. }
  38. }
  39. int main() {
  40. // freopen("input.txt", "r", stdin);
  41. int T;
  42. int a, b;
  43. char op;
  44. scanf("%d", &T);
  45. while (T --) {
  46. scanf("%d %d", &n, &m);
  47. init();
  48. for (int i = 1; i <= m; i ++) {
  49. scanf(" %c %d %d", &op, &a, &b);
  50. if (op == 'A') {
  51. int x = find(a);
  52. int y = find(b);
  53. if (x != y) {
  54. printf("Not sure yet.\n");
  55. continue;
  56. }
  57. if (rela[a] == rela[b]) {
  58. printf("In the same gang.\n");
  59. continue;
  60. } else {
  61. printf("In different gangs.\n");
  62. continue;
  63. }
  64. } else {
  65. unin(a, b);
  66. }
  67. }
  68. }
  69. return 0;
  70. }

poj 1988 堆叠问题

题意:
当前节点所在集合放到目标节点所在集合上,询问当前节点下面有多少节点

思路:
cnt[i] 记录该集合是第几个加入该集合的
sum[i] 该集合中元素个数
注意 cnt[i] 初始化为 0, 方便后面路径压缩时关系递推

  1. /*
  2. ID: LinKArftc
  3. PROG: 1988.cpp
  4. LANG: C++
  5. */
  6. #include <cstdio>
  7. #include <iostream>
  8. #include <cstring>
  9. #include <algorithm>
  10. using namespace std;
  11. const int maxn = 30010;
  12. int father[maxn];
  13. int cnt[maxn]; // 第几个加入该集合
  14. int sum[maxn]; // 该集合中元素个数
  15. void init() {
  16. for (int i = 0; i < maxn; i ++) {
  17. father[i] = i;
  18. sum[i] = 1;
  19. cnt[i] = 0; //从 0 开始方便 line41 计算
  20. }
  21. }
  22. int find(int a) {
  23. if (a != father[a]) {
  24. int fa = father[a];
  25. father[a] = find(fa);
  26. cnt[a] += cnt[fa];
  27. }
  28. return father[a];
  29. }
  30. void unin(int a, int b) {
  31. int x = find(a);
  32. int y = find(b);
  33. if (x != y) {
  34. father[y] = x;
  35. cnt[y] += sum[x];
  36. sum[x] += sum[y];
  37. }
  38. }
  39. int main() {
  40. // freopen("input.txt", "r", stdin);
  41. init();
  42. int n, p;
  43. char op;
  44. int a, b;
  45. scanf("%d", &p);
  46. while (p --) {
  47. scanf(" %c", &op);
  48. if (op == 'M') {
  49. scanf("%d %d", &a, &b);
  50. unin(a, b);
  51. } else {
  52. scanf("%d", &a);
  53. int x = find(a);
  54. printf("%d\n", sum[x] - cnt[a] - 1);
  55. }
  56. }
  57. return 0;
  58. }

poj 1308 判断一个图是不是树

这题有个大坑,0 0 也就是空树也是树。。哔了狗了。。。

  1. /*
  2. ID: LinKArftc
  3. PROG: 1308.cpp
  4. LANG: C++
  5. */
  6. #include <cstdio>
  7. #include <iostream>
  8. #include <cstring>
  9. #include <algorithm>
  10. using namespace std;
  11. const int maxn = 1010;
  12. int father[maxn];
  13. int vis[maxn];
  14. void init() {
  15. for (int i = 1; i < maxn; i ++) father[i] = i;
  16. memset(vis, 0, sizeof(vis));
  17. }
  18. int find(int a) {
  19. if (a != father[a]) father[a] = find(father[a]);
  20. return father[a];
  21. }
  22. void unin(int a, int b) {
  23. int x = find(a);
  24. int y = find(b);
  25. if (x != y) father[y] = x;
  26. }
  27. int main() {
  28. // freopen("input.txt", "r", stdin);
  29. int caseNo = 1;
  30. int a, b;
  31. init();
  32. bool flag = false;
  33. while (~scanf("%d %d", &a, &b)) {
  34. if (a == -1 && b == -1) break;
  35. if (a == 0 && b == 0) {
  36. if (flag == false) {
  37. int cnt = 0;
  38. for (int i = 1; i < maxn; i ++) {
  39. if (vis[i] && (i == father[i])) cnt ++;
  40. }
  41. if (cnt <= 1) printf("Case %d is a tree.\n", caseNo ++);
  42. else printf("Case %d is not a tree.\n", caseNo ++);
  43. }
  44. init();
  45. flag = false;
  46. continue;
  47. }
  48. if (flag) continue;
  49. vis[a] = 1;
  50. vis[b] = 1;
  51. int x = find(a);
  52. int y = find(b);
  53. if (a == b || (a != b && x == y)) {
  54. printf("Case %d is not a tree.\n", caseNo ++);
  55. flag = true;
  56. } else {
  57. unin(a, b);
  58. }
  59. }
  60. return 0;
  61. }

poj 2524 宗教信仰

并查集的最简单模型了

  1. /*
  2. ID: LinKArftc
  3. PROG: 2524.cpp
  4. LANG: C++
  5. */
  6. #include <cstdio>
  7. #include <iostream>
  8. #include <cstring>
  9. #include <algorithm>
  10. using namespace std;
  11. int father[50005];
  12. int find(int x) {
  13. if (x != father[x]) father[x] = find(father[x]);
  14. return father[x];
  15. }
  16. int main() {
  17. int n, m;
  18. int i, j;
  19. int num;
  20. int a, b;
  21. j = 1;
  22. while (cin >> n >> m && (n || m)) {
  23. num = n;
  24. for (i = 0; i < n; i ++) father[i] = i;
  25. for (i = 0; i < m; i++) {
  26. cin >> a >> b;
  27. int x = find(a);
  28. int y = find(b);
  29. if (x != y) {
  30. father[y] = x;
  31. num --;
  32. }
  33. }
  34. printf("Case %d: %d\n", j, num);
  35. j ++;
  36. }
  37. return 0;
  38. }

poj 2492 臭虫中的同性恋

相对于食物链的三性 两性问题比较简单

  1. /*
  2. ID: LinKArftc
  3. PROG: 2492.cpp
  4. LANG: C++
  5. */
  6. #include <cstdio>
  7. #include <iostream>
  8. #include <cstring>
  9. #include <algorithm>
  10. using namespace std;
  11. const int maxn = 2010;
  12. int n, q;
  13. int father[maxn];
  14. int rela [maxn]; // 0 same, 1 dif
  15. void init() {
  16. for (int i = 1; i <= n; i ++) father[i] = i;
  17. memset(rela, 0, sizeof(rela));
  18. }
  19. int find(int a) {
  20. if (a != father[a]) {
  21. int fa = father[a];
  22. father[a] = find(father[a]);
  23. rela[a] = (rela[a] + rela[fa]) % 2;
  24. }
  25. return father[a];
  26. }
  27. void unin(int a, int b) {
  28. int x = find(a);
  29. int y = find(b);
  30. if (x != y) {
  31. father[x] = y;
  32. rela[x] = (rela[a] + rela[b] + 1) % 2;
  33. }
  34. }
  35. int main() {
  36. // freopen("input.txt", "r", stdin);
  37. int T, caseNo = 1;
  38. int a, b;
  39. scanf("%d", &T);
  40. while (T --) {
  41. printf("Scenario #%d:\n", caseNo ++);
  42. scanf("%d %d", &n, &q);
  43. init();
  44. bool flag = false;
  45. for (int i = 1; i <= q; i ++) {
  46. scanf("%d %d", &a, &b);
  47. if (flag) continue;
  48. int x = find(a);
  49. int y = find(b);
  50. if (x == y) {
  51. if (rela[a] == rela[b]) {
  52. flag = true;
  53. printf("Suspicious bugs found!\n\n");
  54. continue;
  55. }
  56. }
  57. else unin(a, b);
  58. }
  59. if (flag == false) printf("No suspicious bugs found!\n\n");
  60. }
  61. return 0;
  62. }

poj 2236 无线网络

很久之前写的了

  1. /*************************************************************************
  2. > File Name: 2236.cpp
  3. > Author: LinKArftc
  4. > Created Time: 2015/6/4 LinK 01:32:24
  5. *************************************************************************/
  6. #include <cstdio>
  7. #include <iostream>
  8. #include <cstring>
  9. #include <algorithm>
  10. #include <cmath>
  11. using namespace std;
  12. #define MAXN 1010
  13. int n, rank[MAXN], pa[MAXN], rep[MAXN];
  14. struct Node {
  15. int x, y;
  16. } pos[MAXN];
  17. void init() {
  18. for (int i = 1; i <= n; i ++) {
  19. rank[i] = 0;
  20. pa[i] = i;
  21. }
  22. }
  23. int find(int x) {
  24. if (x != pa[x]) pa[x] = find(pa[x]);
  25. return pa[x];
  26. }
  27. void uion(int x, int y) {
  28. x = find(pa[x]);
  29. y = find(pa[y]);
  30. if (x != y) {
  31. if (rank[x] < rank[y]) pa[x] = y;
  32. else {
  33. pa[y] = x;
  34. if (rank[x] == rank[y]) rank[x] ++;
  35. }
  36. }
  37. }
  38. double dis(int a, int b) {
  39. Node x, y;
  40. x = pos[a];
  41. y = pos[b];
  42. return sqrt(1.0*(x.x-y.x)*(x.x-y.x) + 1.0*(x.y-y.y)*(x.y-y.y));
  43. }
  44. int main() {
  45. // freopen("input.txt", "r", stdin);
  46. char c;
  47. int x, y, cnt;
  48. double d;
  49. scanf("%d%lf", &n, &d);
  50. for (int i = 1; i <= n; i ++) scanf("%d %d", &pos[i].x, &pos[i].y);
  51. cnt = 0;
  52. init();
  53. while (~scanf(" %c %d", &c, &x)) {
  54. if (c == 'O') {
  55. for (int i = 0; i < cnt; i ++) {
  56. if (dis(rep[i], x) <= d) uion(rep[i], x);
  57. }
  58. rep[cnt ++] = x;
  59. } else {
  60. scanf("%d", &y);
  61. if (find(x) == find(y)) printf("SUCCESS\n");
  62. else printf("FAIL\n");
  63. }
  64. }
  65. return 0;
  66. }

poj 1611 疑似患者

简单,很久之前写的代码,稍微改下代码风格

  1. /*
  2. ID: LinKArftc
  3. PROG: 1611.cpp
  4. LANG: C++
  5. */
  6. #include <cstdio>
  7. #include <iostream>
  8. #include <cstring>
  9. #include <algorithm>
  10. using namespace std;
  11. const int maxn = 30010;
  12. int father[maxn];
  13. int tmp[maxn];
  14. int find(int x) {
  15. if (x != father[x]) father[x] = find(father[x]);
  16. return father[x];
  17. }
  18. void unin(int a, int b) {
  19. int x = find(a);
  20. int y = find(b);
  21. if (x != y) father[x] = y;
  22. }
  23. int main() {
  24. // freopen("input.txt", "r", stdin);
  25. int n, k;
  26. while (~scanf("%d %d", &n, &k)) {
  27. if (n == 0 && k == 0) break;
  28. for (int i = 0; i < n; i ++) father[i] = i;
  29. for (int i = 1; i <= k; i ++) {
  30. int m, t1, t2;
  31. scanf("%d %d", &m, &t1);
  32. for (int j = 2; j <= m; j ++) {
  33. scanf("%d", &t2);
  34. unin(t1, t2);
  35. }
  36. }
  37. int ans = 1;
  38. for (int i = 1; i < n; i ++) {
  39. if (find(i) == find(0)) ans ++;
  40. }
  41. printf("%d\n", ans);
  42. }
  43. return 0;
  44. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注