您的位置:首页 > 其它

HDU1272 小希的迷宫 并查集

2014-07-24 16:23 465 查看
参考网址:http://blog.sina.com.cn/s/blog_6827ac4a0100nyjy.html

解题思路:

由于这里出现的数字不一定连续的数字都会出现,所以设一个mark来标记数字是否出现过。每次输入一对数字的关系则进行查找根结点的函数,即:

if(aa==bb)

{

flag=1;

}

如果能查找到根结点就证明二者已经是相通的,再输入二者的关系就变成有多条相通的路径了。如果不能查找到根结点则继续标记他们的关系。到一个数据输入结束后,再进行判断,是否输入的关系每个数字之间都有相通的路径,即:

for(i=min;i<=max;i++)

{

if(mark[i]&&set[i]==i)

cnt++;

}

if(cnt==1)

printf("Yes\n");

else

printf("No\n");

}

这里还有一个小注意:如果每测试数据已0 0输入,也应打印出Yes

参考代码:

#include<stdio.h>
#include<algorithm>
using namespace std;

int set[100010];
bool mark[100010];
int flag;
int find(int x)
{
int r=x;
while(set[r]!=r)
r=set[r];
return r;

}
void merge(int x,int y)
{
int fx,fy;
fx=find(x);
fy=find(y);
if(fx!=fy)
set[fx]=fy;

}
//每次都是这两个函数的出现,
//就一个是寻找根节点的,还有一个是
//连接两个树的函数,过程相对复杂
//感觉自己是要牢记这两个函数了,无奈啊
int main()
{
int a,b,i;
while(scanf("%d%d",&a,&b)!=EOF)
//本题的输入还是采取一贯的模式作风,
//这个也是值得自己学习的东西的,
//不能因为输入格式的变而改变了自己的书写套路
{
flag=0;int cnt=0;
if(a==-1&&b==-1)break;

if(a==0&&b==0)
{
printf("Yes\n");
continue;
}

for(i=0;i<100010;i++)
{
set[i]=i;
mark[i]=0;
}
//两个初始化,一个用来标记根节点,
//还有一个用来初始化为0
int max=-1,min=100010;
while(a||b)//当a,b均不为0时的标记
{
if(a>max)max=a;
if(b>max)max=b;
if(a<min)min=a;
if(b<min)min=b;//将最大值和最小值都标记出来
mark[a]=1;
mark[b]=1;//当一个数有输入之后就选择将它打好标记
int aa=find(a);
int bb=find(b);
if(aa==bb)
{
flag=1;
}
//用来判断两个数是不是一组的,如果是的话就不进行操作了,
//如果不是那么我们就要选择让他们联通成为一棵树
else
merge(aa,bb);
scanf("%d%d",&a,&b);
}
if(flag==1)
printf("No\n");
else
{
for(i=min;i<=max;i++)
{
if(mark[i]&&set[i]==i)
cnt++;

}
if(cnt==1)
printf("Yes\n");
else
printf("No\n");
}

}
return 0;
}

当然,这里还有一个非常相像的题目叫

hdu 1325 Is It A Tree?(并查集)

就是一个有向图和一个无向图的区别

代码

#include <stdio.h>

const int max_num = 100000+10;
struct Node//Node
{
int num,root,conn;//数据、根、入度
} node[max_num];//node[max_num];

//Node node[max_num];

void init()//这个应该算得上是一个初始化的函数了,值得参考
{
for(int i = 0; i < max_num; i++)
{
node[i].conn = 0;//入度初始化为0
node[i].root= i;//根记录为自身
node[i].num=0;//标记数字是否被使用过,0:没有被使用过,1:使用过了
}
}

int find_root(int a)//一个寻找根的函数,返回值是自己的根节点的值
{
if(node[a].root!=a)
return node[a].root = find_root(node[a].root);
return node[a].root;
}

void union_set(int a,int b)//一个合并两棵树的函数,在两个节点不是同一个的时候使用
{
a = find_root(a);
b = find_root(b);
if(a==b)//同一个根,说明是在同一个树下
return;
node[b].root=a;//把b的根赋为a的根,此时a已经是根,num==root
}

int main()
{
int n,m;
int i = 1;
bool flag=true;//true:是个树,false:不是树
init();
while(scanf("%d%d",&n,&m)!=EOF&&n>=0&&m>=0)//当输入的值为-1时,循环结束
{
if(!flag&&n!=0&&n!=0)continue;//已经确定不是树了,就继续循环
if(n==0&&m==0)
{
int root_num=0;
for(int j = 1; j < max_num;j++)
{
//判断是否为森林,如果,root_num用来记录根的数目
if(node[j].num && find_root(j)==j)//执行条件,被使用过而且还是根节点
root_num++;
if(node[j].conn>1)//如果出现某个节点的入度超过1,不是树。这个标记打的好
{
flag = false;
break;
}
}
if(root_num>1)//连通分支大于1,是森林不是树
flag=false;
if(flag)
printf("Case %d is a tree.\n",i++);
else printf("Case %d is not a tree.\n",i++);
flag = true;
init();
continue;
}
if(m!=n&&find_root(n)==find_root(m))//而且操作是不能重复的
flag = false;
else
{
//将m,n,记录为节点
node[m].num = 1;
node
.num = 1;
node[m].conn++;//入度增加一
union_set(n,m);
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: