20200423并查集总结

并查集是一种用于分离集合操作的抽象数据类型,用于处理集合之间的关系,即动态地维护和处理集合元素之间复杂的关系。
例如:初始时n个元素分属不同的n个集合,通过不断的给出元素之间的联系,要求实时的统计元素间的关系(是否存在直接或间接的联系)。这时就有了并查集的用武之地了。元素间是否有联系只要判断两个元素是否属于同一个集合;而给出的元素间的联系,建立这种联系,则只需要合并两个元素各自所属的集合。这些操作都是并查集提供的。
初始化

for(i=1;i<=n;i++)
    father[i]=i;

寻找根节点编号&&压缩路径

int find(int x)
{
    if(father[x]!=x)
        father[x]=find(father[x]);
    return father[x];
}

合并两个集合

void unionn(int x,int y)
{
    x=find(x);
    y=find(y);
    father[y]=x;
}

判断元素是否属于同一集合

bool judge(int x,int y)
{
    x=find(x);
    y=find(y);
    if(x==y) return true;
    else return false;
}

1346:【例4-7】亲戚(relation)

【题目描述】
或许你并不知道,你的某个朋友是你的亲戚。他可能是你的曾祖父的外公的女婿的外甥女的表姐的孙子。如果能得到完整的家谱,判断两个人是否是亲戚应该是可行的,但如果两个人的最近公共祖先与他们相隔好几代,使得家谱十分庞大,那么检验亲戚关系实非人力所能及。在这种情况下,最好的帮手就是计算机。为了将问题简化,你将得到一些亲戚关系的信息,如Marry和Tom是亲戚,Tom和Ben是亲戚,等等。从这些信息中,你可以推出Marry和Ben是亲戚。请写一个程序,对于我们的关于亲戚关系的提问,以最快的速度给出答案。

【输入】
输入由两部分组成。
第一部分以N,M开始。N为问题涉及的人的个数(1≤N≤20000)。这些人的编号为1,2,3,…, N。下面有M行(1≤M≤1000000),每行有两个数ai,biai,bi,表示已知aiai和bibi是亲戚。
第二部分以Q开始。以下Q行有Q个询问(1≤ Q ≤1000000),每行为ci,dici,di,表示询问cici和didi是否为亲戚。
【输出】
对于每个询问ci,dici,di,输出一行:若cici和didi为亲戚,则输出“Yes”,否则输出“No”。
【输入样例】
10 7
2 4
5 7
1 3
8 9
1 2
5 6
2 3
3
3 4
7 10
8 9
【输出样例】
Yes
No
Yes
这个代码我觉得跟书上基本一样了,咱也搞不清楚为啥一直90分,只能先贴一个90分的代码上来了

#include<iostream>
#include<cstdio>
using namespace std;
const int maxn = 2e4+5;
int fa[maxn];
int find(int x)
{
    return fa[x] == x ? x : fa[x] = find(fa[x]);
}
void unite(int x, int y)
{
    int x1 = find(x);
    int y1 = find(y);
    if (x1 != y1)fa[x1] = y1;
}
int main()
{
    int n, m, x, y;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i)fa[i] = i;
    while (m--)
    {
        scanf("%d%d", &x, &y);
        unite(x, y);
    }
    scanf("%d", &m);
    while (m--)
    {
        scanf("%d%d", &x, &y);
        if (find(x) == find(y))printf("Yes\n");
        else printf("No\n");
    }
}

在这里插入图片描述
[1347:【例4-8】格子游戏]

【题目描述】
或许你并不知道,你的某个朋友是你的亲戚。他可能是你的曾祖父的外公的女婿的外甥女的表姐的孙子。如果能得到完整的家谱,判断两个人是否是亲戚应该是可行的,但如果两个人的最近公共祖先与他们相隔好几代,使得家谱十分庞大,那么检验亲戚关系实非人力所能及。在这种情况下,最好的帮手就是计算机。为了将问题简化,你将得到一些亲戚关系的信息,如Marry和Tom是亲戚,Tom和Ben是亲戚,等等。从这些信息中,你可以推出Marry和Ben是亲戚。请写一个程序,对于我们的关于亲戚关系的提问,以最快的速度给出答案。

【输入】
输入由两部分组成。
第一部分以N,M开始。N为问题涉及的人的个数(1≤N≤20000)。这些人的编号为1,2,3,…, N。下面有M行(1≤M≤1000000),每行有两个数ai,biai,bi,表示已知aiai和bibi是亲戚。
第二部分以Q开始。以下Q行有Q个询问(1≤ Q ≤1000000),每行为ci,dici,di,表示询问cici和didi是否为亲戚。
【输出】
对于每个询问ci,dici,di,输出一行:若cici和didi为亲戚,则输出“Yes”,否则输出“No”。
【输入样例】
10 7
2 4
5 7
1 3
8 9
1 2
5 6
2 3
3
3 4
7 10
8 9
【输出样例】
Yes
No
Yes

#include<bits/stdc++.h>
using namespace std
struct node
{
    int x,y; 
}f[350][350];
node findth(node k)
{
    if((f[k.x][k.y].x==k.x)&&(f[k.x][k.y].y==k.y)){
        return k;
    }
    return f[k.x][k.y]=findth(f[k.x][k.y]);
}
int main()
{
    int n,m;
    scanf("%d %d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            f[i][j].x=i;
            f[i][j].y=j;
        }
    }
    node k1,k2;
    for(int i=1;i<=m;i++){
        int x;
        int y;
        char c;
        scanf("%d %d %c\n",&x,&y,&c);
        if(c=='D'){
            k1=findth(f[x][y]);
            k2=findth(f[x+1][y]);
        }
        if(c=='R'){
            k1=findth(f[x][y]);
            k2=findth(f[x][y+1]);
        }
        if((k1.x==k2.x)&&(k1.y==k2.y))
        {
            printf("%d\n",i);
            return 0;
        }
        else f[k2.x][k2.y]=k1;
    }
    printf("draw\n");
    return 0;
}

1390:食物链【NOI2001】
提交数: 1768 通过数: 887
【题目描述】
动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。
现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这N个动物所构成的食物链关系进行描述:
第一种说法是"1 X Y",表示X和Y是同类。
第二种说法是"2 X Y",表示X吃Y。
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
1)当前的话与前面的某些真的话冲突,就是假话;
2)当前的话中X或Y比N大,就是假话;
3)当前的话表示X吃X,就是假话。
你的任务是根据给定的N(1≤ N ≤50,000)和K句话(0≤K≤100,000),输出假话的总数。
【输入】
第一行是两个整数N和K,以一个空格分隔。
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。
【输出】
只有一个整数,表示假话的数目。
【输入样例】
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
【输出样例】
3
【提示】
在这里插入图片描述

#include<iostream>
#include<cstdio>
using namespace std;
const int N = 1e6;
int p[N];
int findth(int x)
{
	if (x == p[x]) return p[x] = x;
	return p[x] = findth(p[x]);
}
void unionn(int x, int y)
{
	int xx = findth(x);
	int yy = findth(y);
	if (xx != yy) p[yy] = xx;
}
bool f(int x, int y)
{
	return findth(x) == findth(y);
}
int main()
{
	int n, k;
	scanf("%d %d", &n, &k);
	for (int i = 1;i <= 3 * n;i++) p[i] = i;
	int flag = 0;
	for (int i = 0;i < k;i++) {
		int b, x, y;
		scanf("%d %d %d", &b, &x, &y);
		if (x > n || y > n) flag++;
		else {
			if (b == 1) {
				if (f(x, y + n) || f(x, y + n * 2)) flag++;
				else {
					unionn(x, y);//同一类型放在一个集合
					unionn(x + n, y + n);
					unionn(x + 2 * n, y + 2 * n);
				}
			}
			else if (b == 2) {
				if (f(x, y) || f(x, y + 2 * n)) flag++;
				else {
					unionn(x, y + n);//这里是把x吃y换成y吃x的操作
					unionn(x + n, y + 2 * n);
					unionn(x + 2 * n, y);
				}
			}
		}
	}
	printf("%d\n", flag);
	return 0;
}
原创文章 25 获赞 38 访问量 839

猜你喜欢

转载自blog.csdn.net/weixin_46434074/article/details/105703486