- 数据结构并查集的学习
文章目录
- 种类并查集
- 实现
- 例题
- P1892 [BOI2003]团伙
- 题目描述
- 题解
- [NOI2001] 食物链
- 题目描述
- 题解
种类并查集
种类并查集是拓展并查集的一种应用。普通并查集主要解决的是朋友的朋友是朋友的一类问题。而种类并查集则要解决敌人的敌人是朋友这样的一类问题。解决复杂的关系。
实现
多一个种族就开一倍的空间就可以了。
如敌人的敌人是朋友那么就只有2个种族就可以1-n表示一类,n-2n表示另一个类。
不同类中就是敌人同一个类就是朋友
简单来说就是有多少类就开多少倍的空间就可以了。
例题
P1892 [BOI2003]团伙
题目描述
现在有 n n n 个人,他们之间有两种关系:朋友和敌人。我们知道:
- 一个人的朋友的朋友是朋友
- 一个人的敌人的敌人是朋友
现在要对这些人进行组团。两个人在一个团体内当且仅当这两个人是朋友。请求出这些人中最多可能有的团体数。
输入格式
第一行输入一个整数 n n n 代表人数。
第二行输入一个整数 m m m 表示接下来要列出 m m m 个关系。
接下来 m m m 行,每行一个字符 o p t opt opt 和两个整数 p , q p,q p,q,分别代表关系(朋友或敌人),有关系的两个人之中的第一个人和第二个人。其中 o p t opt opt 有两种可能:
- 如果 o p t opt opt 为
F
,则表明 p p p 和 q q q 是朋友。 - 如果 o p t opt opt 为
E
,则表明 p p p 和 q q q 是敌人。
输出格式
一行一个整数代表最多的团体数。
样例 #1
样例输入 #1
6
4
E 1 4
F 3 5
F 4 6
E 1 2
样例输出 #1
3
提示
对于 100 % 100\% 100% 的数据, 2 ≤ n ≤ 1000 2 \le n \le 1000 2≤n≤1000, 1 ≤ m ≤ 5000 1 \le m \le 5000 1≤m≤5000, 1 ≤ p , q ≤ n 1 \le p,q \le n 1≤p,q≤n。
题解
同类很好理解就用并查集就可以解决。
那么敌人的敌人怎么解决呢。
我们在开一倍空间来代表敌人。
如果 a 和 b 是敌人那么:
- 让 a+n 指向 b
- 让 b+n 指向 a
当在来一次的时候就会出现(如 a 和 c 是敌人)
- a+n 和 c 合并 ,这时 c就和b一个集合了
- c+n 指向 a
java代码
package com.yu;import java.io.*;
import java.util.stream.IntStream;public class Main {static final StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));public static String next() throws IOException {st.nextToken();return st.sval;}public static int nextInt() throws IOException {st.nextToken();return (int) st.nval;}public static int[] a;public static int find(int i) {if (a[i] == i) return i;else return a[i] = find(a[i]);}public static void union(int i, int j) {int x = find(i);int y = find(j);if (x != y) a[x] = y;}public static void main(String[] args) throws IOException {int n = nextInt();int k = nextInt();a = IntStream.range(0, 2 * n + 2).toArray();for (int i = 0; i < k; i++) {String opt = next();int p = nextInt();int q = nextInt();if ("E".equals(opt)) {union(q + n, p);union(p + n, q);} else {union(p, q);}}int ans = 0;for (int i = 1; i <= n; i++) {if (find(i) == i) ans++;}System.out.println(ans);}
}
[NOI2001] 食物链
题目描述
动物王国中有三类动物 A , B , C A,B,C A,B,C,这三类动物的食物链构成了有趣的环形。 A A A 吃 B B B, B B B 吃 C C C, C C C 吃 A A A。
现有 N N N 个动物,以 1 ∼ N 1 \sim N 1∼N 编号。每个动物都是 A , B , C A,B,C A,B,C 中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这 N N N 个动物所构成的食物链关系进行描述:
- 第一种说法是
1 X Y
,表示 X X X 和 Y Y Y 是同类。 - 第二种说法是
2 X Y
,表示 X X X 吃 Y Y Y。
此人对 N N N 个动物,用上述两种说法,一句接一句地说出 K K K 句话,这 K K K 句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
- 当前的话与前面的某些真的话冲突,就是假话;
- 当前的话中 X X X 或 Y Y Y 比 N N N 大,就是假话;
- 当前的话表示 X X X 吃 X X X,就是假话。
你的任务是根据给定的 N N N 和 K K K 句话,输出假话的总数。
输入格式
第一行两个整数, N , K N,K N,K,表示有 N N N 个动物, K K K 句话。
第二行开始每行一句话(按照题目要求,见样例)
输出格式
一行,一个整数,表示假话的总数。
样例 #1
样例输入 #1
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
样例输出 #1
3
提示
对于全部数据, 1 ≤ N ≤ 5 × 1 0 4 1\le N\le 5 \times 10^4 1≤N≤5×104, 1 ≤ K ≤ 1 0 5 1\le K \le 10^5 1≤K≤105。
题解
这个有ABC三个种类,所以我们想要开 3 ∗ n 3*n 3∗n 的空间。
我们可以令 1 ∗ n 1*n 1∗n 空间为本, 2 ∗ n 2*n 2∗n 空间为食物(被吃), 3 ∗ n 3*n 3∗n 空间为天敌(吃他的)
所以对于x来说。
[x]所在集合就是x的同类
[x+n]所在集合就是x的食物
[x+2*n] 所在集合就是x的天敌。
操作来说
如果 x x x 吃 y y y 那么
- x 的食物是 y
- x 的天敌是 y 的食物
- y 的天敌是 x
如果 x x x 是 y y y 的同类,那么
- x 的食物不是 y
- x 的天敌也不是 y
java实现代码
package com.yu;import java.io.*;
import java.util.stream.IntStream;public class Main {static final StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));public static int nextInt() throws IOException {st.nextToken();return (int) st.nval;}public static int[] a;public static int find(int i) {if (a[i] == i) return i;else return a[i] = find(a[i]);}public static void union(int i, int j) {int x = find(i);int y = find(j);if (x != y) a[x] = y;}public static void main(String[] args) throws IOException {int n = nextInt();int k = nextInt();a = IntStream.range(0, n * 3 + 3).toArray();int ans = 0;for (int i = 0; i < k; i++) {int j = nextInt();int x = nextInt();int y = nextInt();if (x > n || y > n) {ans++;continue;}if (j == 1) {if (find(x) == find(y + n) || find(x) == find(y + 2 * n)) ans++;else {union(x, y);union(x + n, y + n);union(x + 2 * n, y + 2 * n);}} else {if (find(x) == find(y) || find(x) == find(y + n) || x == y) ans++;else {union(x, y + 2 * n);union(x + n, y);union(y + n, x + 2 * n);}}}System.out.println(ans);}
}