题目链接:http://poj.org/problem?id=1308#top (写这题的时候杭电进不去)
题意: 问你输入的一个图是不是一个树,输入n和m表示n和m是连同的,输入0 0表示这个图已经输完可以进行判断,输入-1 -1表示这个程序运行结束。不过写这题的要注意:空树也是树,死人也是人
思路:这题有两个解法,先写并查集的解法,一般人拿到题目分析的时候都是从并查集入手,但是第二个方法就相对来说就比较简单了。
一、基础并查集
并查集三步:根节点,find,unite
然后考虑题目要求:
1、判断是否成环,这个时候就可以利用临近成环的那一刻出现的情况,要成环的接口处两个节点的根节点相同
2、空树,只要知道了这点应该不难判断
3、未连同,如果出现两个及以上的当标记位置不为零时,节点和父节点一样就处于未连同状态
代码:
#include<cstdio>
#include<cstring>
using namespace std;
const int max=50050;
int flag[max],pa[max],t;
inline void init(){
for(int i=1;i<max;i++)
pa[i]=i;
}
int find(int x){
if(x!=pa[x]) pa[x]=find(pa[x]);
return pa[x];
}
void unite(int x,int y){
int fx=find(x);
int fy=find(y);
if(fx==fy){
t=1;return ;
}
flag[x]=flag[y]=1;
pa[y]=fx;
}
int main(){
int n,m,l=1,ma,mi;
t=0;
memset(flag,0,sizeof(flag));
init();ma=mi=0;
while(~scanf("%d%d",&n,&m)){
if(n>ma) ma=n;if(m>ma) ma=m;
if(n<mi) mi=n;if(m<mi) mi=m;
if(n==-1&&m==-1) break;
if(n==0&&m==0){
int p=0;
for(int i=mi;i<=ma;i++)
if(flag[i]!=0&&i==pa[i]) p++;
if(p>1) t=1;
memset(flag,0,sizeof(flag));
if(t==0) printf("Case %d is a tree.\n",l);
else printf("Case %d is not a tree.\n",l);
init();l++;t=ma=mi=0;
continue;
}
if(flag[n]==0&&flag[m]!=0) unite(m,n);
else unite(n,m);
}
return 0;
}
二、图的性质
这个也算是这题当中树的性质吧。我们可以把这个树看成图(怎么写的那么便扭呢。。。),这个图由节点(dian)和边(bian)组成,当这个图输完的时候
若dian>bian+1
,说明这个图还有的位置是未连同的;
若dian<bian+1
, 说明这个图里成环;
所以判断成树的条件就bian+1=dian;
又因为空树的存在,加上判断条件bian=0;
代码:
#include<cstdio>
#include<cstring>
const int max=50500;
int main(){
int flag[max],dian,bian,max,min;
dian=bian=min=max=0;
int n,m,l=1;
memset(flag,0,sizeof(flag));
while(~scanf("%d%d",&n,&m)){
if(n==-1&&n==-1) break;
if(n==0&&m==0)
{
int sum=0;
memset(flag,0,sizeof(flag));
if(dian==bian+1||bian==0) printf("Case %d is a tree.\n",l);
else printf("Case %d is not a tree.\n",l);
l++;bian=dian=0;
continue;
}
if(n>max) max=n;if(n<min) min=n;
if(m>max) max=m;if(m<min) min=m;
if(flag[n]==0) flag[n]=1,dian++;
if(flag[m]==0) flag[m]=1,dian++;
bian++;
}
return 0;
}