知道“人言可畏”吗?在我们的生活中,尤其在现有的网络上,存在一些广泛传播的谣言。今天我们在一个群体中研究这个问题:
(1)一个群体中存在一些两两之间的朋友关系;
(2)一个人发布“谣言”;
(3)一个人在知道“谣言”时,会告诉他(她)的朋友;
请你判断是否所有人最终都知道谣言。
第一行是一个正整数:测试用例数目,最多为100。之后,每个测试用例包括多行:
第1行给出两个整数(空格分隔),前者表示群体人数n,后者表示“谣言”发布者t,群体成员用整数序号表示,2≤n≤200,0≤t≤n-1
第2行给出一个整数,群体两两存在的朋友关系数m,0≤m≤20100
m行,每行两个整数(空格分隔),表示群体中两个成员存在朋友关系。
读完此题便易知为并查集的范畴。
首先要思考读入的两个关系怎么并入一个集合内,我们可以采用判断他们根节点的方式,即所有数据的必定可以用他们的根节点表示,如已知1-2-3
那么我们想判断读入的1-3是否处在同一树上,只需看根节点,1的根节点为3,3的根节点是它本身3,那么自然在同一棵树上。那如何寻找他的根节点呢?
我们可以一步步向上迭代寻找每个节点的上一个节点
此部分代码如下:
int i=x;
while(pre[i]!=i)
i=pre[i];
这样根节点i就被找到了。
如果读入的两个数已经都在同一棵树,那么无需操作,否则就要将两个数合并,代码如下:
void mix(int x,int y)
{
int fx=Find(x);
int fy=Find(y);
if (fx!=fy)
{
pre[fx]=fy;
}
}
这样就有一个问题,我们看到我们寻找根节点是层层向上的,如果树过高势必会影响速度,该怎样处理呢?最快的查找方式自然是每一个节点的上一个节点就是根节点,所以每次查找完数据,我们就将树的高度降低,这个很有意思,代码如下:
while(r!=i)
{
j=pre[r];
pre[r]=i;
r=j;
}
return i;
}
题外话:此题的谣言发布者除了方便理解,似乎没有其他用处,因为关键在于树的链接是否包含了所有数据,只需要保证一定有一谣言发布者即可。
附AC代码:#include<stdio.h>
int pre[20105];
int Find(int x)
{
int i=x;
int r=x;
int j;
while(pre[i]!=i)
i=pre[i];
while(r!=i)
{
j=pre[r];
pre[r]=i;
r=j;
}
return i;
}
void mix(int x,int y)
{
int fx=Find(x);
int fy=Find(y);
if (fx!=fy)
{
pre[fx]=fy;
}
}
int main()
{
int T,t,n,m,i,a,b,temp,ok;
scanf("%d",&T);
while(T--)
{ ok=1;
scanf("%d%d",&n,&t);
for (i=0;i<n;i++)
pre[i]=i;
scanf("%d",&m);
for (i=0;i<m;i++)
{
scanf("%d%d",&a,&b);
mix(a,b);
}
temp=Find(t);
for (i=0;i<n;i++)
if (Find(i)!=temp)
{
ok=0;
break;
}
if (ok)
printf("Yes\n");
else
printf("No\n");
}
return 0;
}