您的位置:首页 > 其它

并查集的反思-----小希的迷宫与Is It a Tree?

2013-04-01 17:32 316 查看
/*
事实证明半斤八两的理解忘记的速度会很快,今天早上起来忽然想起来这个题(因为昨天碰到一个欧拉回路+并查集+字典树),已然思路不清晰了。
是什么什么让我认识并查集的呢?是Hdu的畅通工程,给出一些列已经连通的点,要你判断还需要多少建多少条路使保持畅通。我们解决的办法是将能够保持连通转化为所有的点位于一个集合,并且将他们的祖先序号规定为最小元素的值。然后还需要建的路即是不相交的集合。然后我们又可以做这样一类题,图是否连通,这类题,最小生成树也可以做。而小希的迷宫呢?小希的迷宫是在保持原有图形连通的基础上加入了一个新的条件---不成环。在思考不成环这个问题时,我注意到了原来一直没有透彻理解的问题---路径压缩。如果合并的两个点具有公共祖先,那么将形成以个环。然后,又碰到了这道经典的题目,is it a tree?这个图在小希的迷宫上又增加了一个条件---有向图,很自然不可能由一个节点的子孙走回父亲节点,所以判断入度为1的条件---即不走回头路 find(i)=i.
并查集目前为止一般包括三个函数
void union()//跟据你的输入合并2个点
int find()//寻找祖先+路径压缩
void make()//初始化

无向图的并查集
1.不成回路
2.联通
*/
#include<stdio.h>
#include<string.h>
#define maxn 100002
#define max(a,b) (a)>(b)?(a):(b)
int flag[100001];
int bin[100001];
int ans;
int Find(int x)
{
while(bin[x]!=x)
x=bin[x];
return x;
}
void Union(int x,int y)
{
flag[x]=flag[y]=1;
x=Find(x);
y=Find(y);
if(x!=y)
bin[x]=y;
else
ans=0;//如果祖先相同,则构成回路。太重要了。
}
int main()
{
int a,b,i;
while(scanf("%d%d",&a,&b)!=EOF &&(a!=-1 || b!=-1))
{
memset(flag,0,sizeof(flag));
int count=0;
if(a==0 && b==0)
{
printf("Yes\n");
continue;
}
for(i=0;i<=100001;i++)
bin[i]=i;
//bin[i]=maxn;
//bin[a]=a;
//bin[b]=b;
Union(a,b);
ans=1;
while(scanf("%d%d",&a,&b),a,b)
{
//bin[a]=a;
//bin[b]=b;
Union(a,b);
}
for(i=1;i<=100001;i++)
if(flag[i]&&bin[i]==i)
count++;
if(count>1)
ans=0;//这个判断是否联通。

if(ans)
printf("Yes\n");
else
printf("No\n");
}
return 0;
}


hdu 1325
/*    并查集
题意:输入每两个数a,b, a指向b(即:a为b的父亲),输入0,0代表一棵树结束;
思路:判断是否为树;
1、空树是一棵树
2、除了根节点,每个节点入度必须为1
3、不能有圈
4、不能有连通分量
*/
#include<stdio.h>
#define N 10005

int father
,mark
;

void MakeSet()//初始化
{
for(int i=0;i<N;i++)
{father[i]=i;
mark[i]=0;}
}

int FindSet(int x)
{
if(x!=father[x])
father[x]=FindSet(father[x]);//没有路径压缩实践证明超时
return father[x];
}

bool Union(int a,int b)
{
int x=FindSet(a);
int y=FindSet(b);
if(x==y)
return false;
else
father[y]=x;//被卡了,注意接的顺序
return true;
}

int main()
{
int s,e;
int can=1;
while(scanf("%d%d",&s,&e),s>=0&&e>=0)
{
MakeSet();
Union(s,e);
bool judge=true;
if(s==0&&e==0)//空树也满足条件
{
printf( "Case %d is a tree.\n",can++);
continue;
}
mark[s] = mark[e] = 1;
while(scanf("%d%d",&s,&e),s||e)
{
mark[s]=mark[e]=1;
if(FindSet(e)==e)//判断节点入度是否为1
{
judge*=Union(s,e);//判断是否有圈
}
else
judge=false;
}

int flag=0;
for(int i=0;i<N;i++)
{
if(mark[i])
{
if(FindSet(i)==i)
flag++;
}
}
if(flag==1&&judge)
printf( "Case %d is a tree.\n",can++);
else
printf( "Case %d is not a tree.\n",can++);
}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  并查集