题目来源:HDU 1272
简单分析:
又补一题,题意很简单,判断一个无向图是不是树,满足两个条件就行:1.连通 2.无环
于是乎,并查集就登场了。
首先建立一个空图,每当向其中增加一条边时,判断两个端点是否在连通分量内,如果在,那么就会有环,不能成树。
把边都加入后,再判断这些端点的根节点数目,只有一个才是树,其实树的边满足这个公式:p=q-1,p为边数,q为顶点数。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string.h>
#include <cmath>
#define loop(i,n) for(int i=1;i<=(n);++i)
using namespace std;
const int MAXN=100000+5;
bool vis[MAXN]; //因为编号不是按顺序的,所以要标记那些编号被访问过
int pre[MAXN];
bool flag; //判断是否是树
int findR(int x)
{
while(x!=pre[x])
x=pre[x];
return x;
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
flag=true;
int cnt=0;
loop(i,MAXN)
{
pre[i]=i;
vis[i]=false;
}
if(n==-1&&m==-1)
break;
if(m==0&&n==0) //空树也符合题意,虽然我不知道为什么...
{
printf("Yes\n");
continue;
}
vis[n]=vis[m]=true;
int xx=findR(n); //这里又卡了一会,老是忘记并查集pre数组的用法
int yy=findR(m);
pre[yy]=xx;
while(~scanf("%d%d",&n,&m))
{
if(m==0&&n==0)
break;
vis[n]=vis[m]=true;
if(findR(n)==findR(m)) //如果已经是连通分量,再添入一条边必成环
flag=false;
else
{
xx=findR(n);
yy=findR(m);
pre[yy]=xx;
}
}
loop(i,MAXN)
{
if(vis[i]&&(pre[i]==i)) //计算根节点的数目
cnt++;
}
if(cnt>=2)
flag=false;
if(flag)
printf("Yes\n");
else
printf("No\n");
}
return 0;
}