种类并查集是拓展并查集的一种应用。普通并查集主要解决的是朋友的朋友是朋友的一类问题。而种类并查集则要解决敌人的敌人是朋友这样的一类问题。解决复杂的关系。
多一个种族就开一倍的空间就可以了。
如敌人的敌人是朋友那么就只有2个种族就可以1-n表示一类,n-2n表示另一个类。
不同类中就是敌人同一个类就是朋友
简单来说就是有多少类就开多少倍的空间就可以了。
现在有 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 有两种可能:
F
,则表明 p p p 和 q q q 是朋友。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 和 c 是敌人)
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);
}
}
动物王国中有三类动物 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 句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
你的任务是根据给定的 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 x x 是 y y 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);
}
}