以下为AC代码,思路:用并查集来合并集合就可以了,把每个社团当作一个集合和并。最后,找出0号学生所在的集合的根节点,然后遍历所有学生,如果和0号学生的根节点相同,说明他们在一个集合当中。
#include<iostream>
#include<cstdio>
using namespace std;
int n,m;
const int maxn=30010;
int f[maxn];
void init()
{
for(int i=0;i<n;i++)
f[i]=i;
}
int findf(int u)
{
int a=u;
while(u!=f[u]){u=f[u];}
while(a!=f[a]) { int z=a; a=f[a]; f[z]=u;}
return u;
}
void Union(int u,int v)
{
int t1=findf(u);
int t2=findf(v);
if(t1!=t2) f[t2]=t1;
}
int main()
{
while(cin>>n>>m)
{
if(n==0&&m==0) break;
init();
int sum=0;
while(m--)
{
int k,num,first;
cin>>k>>first;
for(int i=1;i<k;i++)
{
cin>>num;
Union(first,num);
}
}
for(int i=0;i<n;i++)
if(findf(i)==findf(0)) sum++;
// cout<<"&&&&&&&&&&&&&&&&&&&&&&\n";
// for(int i=0;i<n;i++) cout<<"f["<<i<<"]= "<<f[i]<<endl;
// cout<<"###################\n";
cout<<sum<<endl;
}
return 0;
}
下面这个代码有错误:
#include<iostream>
#include<cstdio>
using namespace std;
int n,m;
const int maxn=30010;
int f[maxn];
void init()//初始化并查集
{
for(int i=0;i<n;i++)
f[i]=i;
}
int findf(int u)
{
int a=u;
while(u!=f[u]){u=f[u];}//找到根节点
while(a!=f[a]) { int z=a; a=f[a]; f[z]=u;}//压缩路径
return u;
}
void Union(int u,int v)
{
int t1=findf(u);
int t2=findf(v);
if(t1!=t2) f[t2]=t1;
}
int main()
{
while(cin>>n>>m)
{
if(n==0&&m==0) break;
init();
int sum=0;
while(m--)
{
int k,num,first;
cin>>k>>first;
for(int i=1;i<k;i++)
{
cin>>num;
Union(first,num);
}
}
for(int i=0;i<n;i++)
if(f[i]==f[0])//这里有问题。判断条件出错了。
sum++;
cout<<sum<<endl;
}
return 0;
}
本来我以为,只要合并了,而且压缩路径之后,就一定能使数组里所有元素的值储存根节点的数值,但是这里却不对,无法通过第一个测试样例。(这里我也不太明白,明明《啊哈!算法》里,压缩路径之后,能直接使数组所有的元素储存根结点),所以这里,我采用了访问根节点,并判断是否相等的做法。
如果是访问根节点,来判断是否在同一集合的话,就不用提前压缩路径了。