输入格式:输入包含若干组测试数据,每组测试数据的第一行给出两个用空格隔开的正整数,分别是城镇数目 n 和道路数目 m;随后的 m 行对应 m 条道路,每行给出一对用空格隔开的正整数,分别是该条道路直接相连的两个城镇的编号。简单起见,城镇从 1 到 n 编号。注意:两个城市间可以有多条道路相通。在输入数据的最后,为一行一个整数 0,代表测试数据的结尾。
int f[1005], n, m; intget(int x) { // 1. 并查集的查询,这里我用三元表达式压了下行 return (f[x] == x) ? (x) : (f[x] = get(f[x])); }
intmain() { ios::sync_with_stdio(0); do { cin >> n; if (n == 0) break; cin >> m; for (int i = 1; i <= n; i++) f[i] = i; for (int i = 1; i <= m; i++) { int x, y; cin >> x >> y; f[get(x)] = get(y); // 2. 并查集的合并 } int ans = 0; for (int i = 1; i <= n; i++) ans += (i == get(i)); // 3. 统计集合个数(就是统计根节点个数) cout << ans - 1 << endl; } while (n != 0); return0; }
intmain(){ ios::sync_with_stdio(0); #ifndef ONLINE_JUDGE freopen("data.in", "r", stdin); freopen("data.out", "w", stdout); #endif cin >> n >> m; for (int i = 1;i <= 2 * n;i++) f[i] = i; for (int i = 1;i <= m;i++) { char op; int p, q; cin >> op >> p >> q; if (op == 'F') merge(p, q); elsemerge(n + p, q), merge(n + q, p); } for (int i = 1;i <= n;i++) if (f[i] == i) ans++; cout << ans << endl; return0; }
P2024 [NOI2001] 食物链
题意:动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形:A 吃 B,B 吃 C,C 吃 A。现有 N 个动物,以 1∼N 编号。每个动物都是 A,B,C 中的一种,但是我们并不知道它到底是哪一种。有人用两种说法对这 N 个动物所构成的食物链关系进行描述:
第一种说法,表示 X 和 Y 是同类。
第二种说法表示 X 吃 Y。
此人对 N 个动物,用上述两种说法,一句接一句地说出 K 句话,这 K 句话有的是真的,有的是假的。 当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
当前的话与前面的某些真的话冲突,就是假话;
当前的话中 X 或 Y 比 N 大,就是假话;
当前的话表示 X 吃 X,就是假话。
你的任务是根据给定的 N 和 K 句话,输出假话的总数。
思路:本题可以用带权并查集或者拓展域并查集解决,这里讨论一下拓展域并查集解法。食物链构成环形是解题的关键。这意味着如果 A 吃 B,那么 B 一定吃 C,C 一定吃 A。考虑拓展域并查集,开捕食域与天敌域两个拓展域,加上原域总共需要三倍空间。如果 X 和 Y 是同类,那这句话是假话当且仅当 X 吃 Y 或者 Y 吃 X,不是假话则把三个域分部合并。X 吃 Y 是假话当且仅当 X 和 Y 是同类或者 Y 吃 X,不是假话也相应合并。具体代码实现细节如下: