并查集这个是偶然的一个晚上理解的,并没有很透彻,但是我发现并查集能够解决很多问题。比如经典的朋友分桌子,流感不同社团判断怀疑对象。接下来我放上几个例子,希望能够给有天赋的你们带来帮助
在我看来,并查集其实做起来是有模板的,他主要就包括三个操作:
1.MAKE-SET(x):即初始化操作,建立一个只包含元素 x 的集合。
通常并查集初始化操作是对每个元素都建立一个只包含该元素的集合。 一般我们把其父节点设为本身。
2.UNION(x, y):即合并操作,将包含 x 和 y 的集合合并为一个新的集合。
点的父亲设置为另一个根结点。这样我们就把两棵有根树合并成一棵了。网上有很多图片助于理解,
在不相交森林中,并查集的查询操作,指的是查找出指定元素所在有根树的根结点是谁。我们可以通过每个指向父结点的边回溯到结点所在有根树的根,也就是对应集合的代表元素。
下面就上例题:howManyTables
package howManyTables;
import java.util.Scanner;
public class Main {
/** How Many Tables
* For each test case, just output how many tables Ignatius needs at least.
2
5 3
1 2
2 3
4 5
5 1
2 5
output
2
4
*/
public static int N=0;
public static int M=0;
public static int a=0;
public static int b=0;
public static int[] father=new int[1100];
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int table=sc.nextInt();
for (int i = 0; i < table; i++) {
N=sc.nextInt();
M=sc.nextInt();
for (int j = 1; j <=N; j++) {
father[j]=j; //初始化它,让编号为一的值为1,编号为2的值为2.以此类推。
}
for (int k = 1; k <= M; k++) {
a=sc.nextInt();
b=sc.nextInt();
join(a,b);
}
int ans=0;
for (int j = 1; j <= N; j++) {
if(father[j]==j)//经过M次合并之后,如果是朋友,或者间接朋友的,他们对应的值都为同一个。所以桌子就减少了。
ans++;//如果值还是对应的,那么就不是朋友关系,增加一张桌子。
}System.out.print(ans);//输出注意格式,只有一个空行。
}
}
private static void join(int a, int b) {
int x,y;
x=Find(a);//假设a=2,b=3,我认为经过这个查找之后p[2]就等于p[3]了
y=Find(b);
if(x!=y){
if(x>y)
father[y]=x;
else
father[x]=y;
}
}
private static int Find(int a) { //这个的作用就是下面的查找。
int r=a;
while(a!=father[a])
a=father[a];
father[a]=r;
return a;
}
}
上面这个题目算
是很经典的了,如果你理解了主要的那三步,就大概了解的大部分了。
pat的level2 排座位例子,也很经典;
L2-010. 排座位
布置宴席最微妙的事情,就是给前来参宴的各位宾客安排座位。无论如何,总不能把两个死对头排到同一张宴会桌旁!这个艰巨任务现在就交给你,对任何一对客人,请编写程序告诉主人他们是否能被安排同席。
输入格式:
输入第一行给出3个正整数:N(<= 100),即前来参宴的宾客总人数,则这些人从1到N编号;M为已知两两宾客之间的关系数;K为查询的条数。随后M行,每行给出一对宾客之间的关系,格式为:“宾客1 宾客2 关系”,其中“关系”为1表示是朋友,-1表示是死对头。注意两个人不可能既是朋友又是敌人。最后K行,每行给出一对需要查询的宾客编号。
这里假设朋友的朋友也是朋友。但敌人的敌人并不一定就是朋友,朋友的敌人也不一定是敌人。只有单纯直接的敌对关系才是绝对不能同席的。
输出格式:
对每个查询输出一行结果:如果两位宾客之间是朋友,且没有敌对关系,则输出“No problem”;如果他们之间并不是朋友,但也不敌对,则输出“OK”;如果他们之间有敌对,然而也有共同的朋友,则输出“OK but...”;如果他们之间只有敌对关系,则输出“No way”。
输入样例:7 8 4 5 6 1 2 7 -1 1 3 1 3 4 1 6 7 -1 1 2 1 1 4 1 2 3 -1 3 4 5 7 2 3 7 2输出样例:
No problem OK OK but... No way
package 排座位;
import java.io.BufferedInputStream;
import java.util.Scanner;
public class Main {
static int[] p = new int[110];
static int[][] pr = new int[110][110];
public static void main(String[] args) {
Scanner sc = new Scanner(new BufferedInputStream(System.in));
int N = sc.nextInt();
int M = sc.nextInt();
int K = sc.nextInt();
for (int i = 1; i <= N; i++) {
p[i] = i;
}
Main main = new Main();
int a = 0;
int b = 0;
int c = 0;
for (int i = 0; i < M; i++) {
a = sc.nextInt();
b = sc.nextInt();
c = sc.nextInt();
pr[a][b] = c ;
pr[b][a] = c;
if(c==1)
main.Union(a, b);
}
int r =0 ;
int l =0 ;
for (int i = 0; i < K; i++) {
r = sc.nextInt();
l = sc.nextInt();
if(pr[r][l]==1){
System.out.println("No problem");
}else if(pr[r][l]==-1){
r = main.Find(r);
l = main.Find(l);
if(l==r){
System.out.println("OK but...");
}else{
System.out.println("No way");
}
}else{
System.out.println("OK");
}
}
sc.close();
}
void Union(int a , int b){
int x = Find(a);
int y = Find(b);
if(x!=y){
if(x > y){
p[y] = x;
}else{
p[x] = y;
}
}
}
int Find(int a) {
while(p[a]!=a){
a = p[a];
}
return a;
}
}