P1551 亲戚+P1536 村村通+P2307迷宫 (并查集)

P1551 亲戚

题解

#include<bits/stdc++.h>
using namespace std;
int fa[5050],rk[5050],n,m,p;
//普通
int find(int x)
{
	if(fa[x]==x)  return x;   //找祖先
	return fa[x]=find(fa[x]);   
}
void merge(int i,int j)
{
	int x=find(i),y=find(j);
	fa[x]=y;                    //普通合并,没规定下x,y谁是祖先     
}

//优化
int find(int x)
{
	return fa[x]==x?x:(fa[x]=find(fa[x]));   //路径压缩优化
}
void merge(int i,int j)
{
	int x=find(i),y=find(j);   
	if(rk[x]<=rk[y])  fa[x]=y;    //按秩(深度)大小排祖先
	else fa[y]=x;                 //深的为祖先
	if(rk[x]==rk[y])  rk[y]++;    //使祖先变深
}
int main()
{
	cin>>n>>m>>p;
	for(int i=1;i<=n;i++)  
	{
		fa[i]=i;
		rk[i]=1;
	}
	for(int i=1;i<=m;i++)
	{
		int x,y;
		cin>>x>>y;
		merge(x,y);
	}
	for(int i=1;i<=p;i++)
	{
		int x,y;
		cin>>x>>y;
		if(find(fa[x])==find(fa[y])) cout<<"Yes"<<endl;
		else cout<<"No"<<endl;
	}
	return 0;
}

详见:https://zhuanlan.zhihu.com/p/93647900   (这位大佬写得很好)

P1536 村村通

题解:

#include<bits/stdc++.h>
using namespace std;
int fa[1010],rk[1010];
int find(int x)
{
	return fa[x]==x?x:(fa[x]=find(fa[x]));
}
void merge(int i,int j)
{
	int x=find(i),y=find(j);
	if(rk[x]<=rk[y])  fa[x]=y;
	else fa[y]=x;
	if(rk[x]==rk[y])  rk[y]++;
}
int main()
{
	int m,n;
	while(cin>>m)
	{
		set<int>s;
		if(m==0) break;
		cin>>n;
		for(int i=1;i<=m;i++)  { fa[i]=i; rk[i]=1;}
		for(int i=1;i<=n;i++)
		{
			int x,y;
			cin>>x>>y;
			merge(x,y);
		}
		for(int i=1;i<=m;i++)   //用set记录路的祖先有几个
		{
			s.insert(find(i));
		}
		cout<<s.size()-1<<endl;    //这些祖先要连起来所需的路
	}
	return 0;
}

最后判断所新建路数也可以这样做:

int ans=0;		
for(int i=1;i<=m;i++)
{
	if(find(i)==i)  ans++;   //若路的祖先是它自己 ,之后只需把这些祖先连起来即可
}
cout<<ans-1<<endl;

P2307 迷宫 

题解:(并查集+记录父节点个数)

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int fa[N],sum,flag;  //sum记录父节点的个数 flag记录是否有违规情况
bool book[N];   //判断房间是否出现过
void cls()
{
	for(int i=1;i<=N;i++) fa[i]=i;
	memset(book,0,sizeof book);
	sum=0,flag=0;
}
int find(int x)
{
	return fa[x]==x?x:fa[x]=find(fa[x]);
}
int main()
{
	cls();
	int x,y;
	while(cin>>x>>y)
	{
		if(x==-1&&y==-1) break;
		if(x==0&&y==0) 
		{
			if(flag==0&&sum==1) cout<<1<<endl;
			else cout<<0<<endl;
			cls(); continue;
		}
		if(!book[x])  book[x]=1,sum++;    //x没出现过,父节点加一
		if(!book[y])  book[y]=1,sum++;
		int xx=find(x),yy=find(y);
		if(xx==yy)   flag=1;   //若x,y父节点相同,说明之前已可以相连,不符合条件
		else  fa[xx]=yy,sum--;            //合并,合并后父节点-1
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值