上次Gardon的迷宫城堡小希玩了很久(见Problem B),现在她也想设计一个迷宫让Gardon来走。但是她设计迷宫的思路不一样,首先她认为所有的通道都应该是双向连通的,就是说如果有一个通道连通了房间A和B,那么既可以通过它从房间A走到房间B,也可以通过它从房间B走到房间A,为了提高难度,小希希望任意两个房间有且仅有一条路径可以相通(除非走了回头路)。小希现在把她的设计图给你,让你帮忙判断她的设计图是否符合她的设计思路。比如下面的例子,前两个是符合条件的,但是最后一个却有两种方法从5到达8。
Input
输入包含多组数据,每组数据是一个以0 0结尾的整数对列表,表示了一条通道连接的两个房间的编号。房间的编号至少为1,且不超过100000。每两组数据之间有一个空行。
整个文件以两个-1结尾。
Output
对于输入的每一组数据,输出仅包括一行。如果该迷宫符合小希的思路,那么输出"Yes",否则输出"No"。
Sample Input
6 8 5 3 5 2 6 4
5 6 0 0
8 1 7 3 6 2 8 9 7 5
7 4 7 8 7 6 0 0
3 8 6 8 6 4
5 3 5 6 5 2 0 0
-1 -1
Sample Output
Yes
Yes
No
解题思路:由三幅图可观,树的结构不能形成环,故两两相连的房间,并在一起时,查找上级(结点)不能是同一个。ps.本题的根节点只能是一个。
#include <iostream>
#include <cstdio>
using namespace std;
int set[100005];
int vis[100005];
int find(int a){
int r=a;
while(set[r]!=r) ///获得是上次merge()后的set[i]
r=set[r];
int i=a;
int j;
while(i!=r){ ///路径压缩
j=set[i];
set[i]=r;
i=j;
}
return r;
}
int merge(int a,int b){
int A,B;
A=find(a); ///返回a,b上级的值
B=find(b);
if(A!=B){ ///确保a,b上级不是同一个
set[B]=A; ///a为b的上级
return 1;
}
else
return 0;
}
void init() ///初始化定义
{
for(int i=0;i<=100005;i++){ ///set[]={0,1,....100005}
set[i]=i;
vis[i]=0; ///标记数组,判断是否被遍历过
}
}
int main(){
int a,b,k=0;
int flag=1;
init();
while(scanf("%d %d",&a,&b)!=EOF){
if(a==-1&&b==-1){
break;
}
if(k==0){ ///特例 只有0 0 此样例结束
if(a==0&&b==0){
printf("Yes\n");
continue;
}
}
k++;
if(a!=0&&b!=0){
vis[a]=vis[b]=1; ///标记表示遍历过
if(merge(a,b)==0){
flag=0; ///发现成环状
}
}
else if(a==0&&b==0){ ///末尾0 0,样例遍历完全,此样例结束
if(flag==0){
printf("No\n");
flag=1;
}
else{
int c=0;
for(int i=0;i<=100005;i++){
if(vis[i]&&set[i]==i) ///可能会有多个根,且彼此不连接
c++;
}
if(c==1) ///只有一个根,满足条件
printf("Yes\n");
else
printf("No\n");
}
k=0;
init();
}
}
return 0;
}
并merge()就是,让一个数b指向另一个数a,它的set数组,set[b]=a,set里存什么,什么就是他的上级除了他本身,当一个数b有上级a了,我们就认为a,b并在一起了
查find()就是,找出a,b要并的时候的set值,确定他们的上级c不一样,否则,连成一个环,b—>a—>c,b—>c
并查集并没有将一棵树直接存起来,而是利用set[],来存储两个节点间的上下级关系a<—b,从而进行查询,判断某个结点是否属于某棵树或结点连接是否成树。