知道“人言可畏”吗?在我们的生活中,尤其在现有的网络上,存在一些广泛传播的谣言。今天我们在一个群体中研究这个问题:
(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; }