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;
}