图论算法 并查集

运用集合的思想,通过集合来对数据进行分类,其实现也非常简单:
对于每个每一个数据都定义f数组表示此数据的祖先(f【i】就代表i所在集合的公共祖先,初始化f为该数据本身,此时每个数据都是一个独立的集合)

两个数据i所在的集合与j所在的集合总合为统一集合时时,只需把数据i所在集合的公共祖先f【i】改为j的所在集合的公共祖先f【j】(即让i,j拥有共同祖先)即可

当查找数据a,b是否在同一集合中时,可查找a,b所在集合的公共祖先并判断是否相等,若相等,则在同一集合当中,反之则不在统一集合当中

可用如下函数实现:
int find(int x)//查找x的祖先{
	if(f[x]!=x)//当x所在集合公共祖先不是x自己时
    f[x]=find(f[x]);//查找x父亲的祖先
	return f[x];//返回x集合的公共祖先
}


int uni(int x,int y)//合并x与y所在的集合{
    int x1=find(x);//查找x集合的公共祖先
    int y1=find(y);
    if(x1!=y1)//判断x1与y1是否相等
    f[y1]=x1;//更改y集合的公共祖先为x1
}
例题:

在某城市里住着n个人,任何两个认识的人不是朋友就是敌人,而且满足:

1、我朋友的朋友是我的朋友;

2、我敌人的敌人是我的朋友;

所有是朋友的人组成一个团伙。告诉你关于这n个人的m条信息,即某两个人是朋友,或者某两个人是敌人,请你编写一个程序,计算出这个城市最多可能有多少个团伙?

【输入】
第1行为n和m,1<n<1000,1≤m≤100 000;

以下m行,每行为p x y,p的值为0或1,p为0时,表示x和y是朋友,p为1时,表示x和y是敌人。

【输出】
一个整数,表示这n个人最多可能有几个团伙。

【输入样例】
6 4
1 1 4
0 3 5
0 4 6
1 1 2
【输出样例】
3
题解:

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
int f[1005],n,m,p,ans,x,y,g[1005],di[1005];

int find(int x)
{
	if(f[x]!=x) f[x]=find(f[x]);
	return f[x];
}
void uni(int x,int y)//将y的祖先更新为x的祖先 
{
	int x1=find(x);
	int y1=find(y);
	if(x1!=y1) f[y1]=x1;
	return;
}
int main()
{
	for(int i=1;i<1005;i++)
	f[i]=i;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&p,&x,&y);
		if(p==0) uni(x,y);
		else
		{
			x=find(x);y=find(y);
			if(di[x]==0) di[x]=y;//如果x无敌人,新建集合存储其敌人y【如果x祖先无敌人,新建集合存储其敌人y的祖先】 
			if(di[y]==0) di[y]=x;
			uni(x,di[y]);//y敌人的集合与x集合合并 【y的祖先的敌人的集合与x祖先集合合并】 
			uni(y,di[x]);
		}
	}
	memset(g,0,sizeof(g));
	for(int i=1;i<=n;i++)
	g[find(i)]=1;
	for(int i=1;i<=n;i++)
	if(g[i]==1) ans++;
	printf("%d",ans);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44336566/article/details/86554245