题意:给定一个包含 N 个顶点 M 条边的无向图 G ,判断 G 是不是一棵树。
输入
第一个是一个整数 T ,代表测试数据的组数。 (1 ≤ T ≤ 10)
每组测试数据第一行包含两个整数 N 和 M 。(2 ≤ N ≤ 500, 1 ≤ M ≤ 100000)
以下 M 行每行包含两个整数 a 和 b ,表示顶点 a 和顶点 b 之间有一条边。(1 ≤ a, b ≤ N)
输出
对于每组数据,输出YES或者NO表示 G 是否是一棵树。
样例输入
2
3 2
3 1
3 2
5 5
3 1
3 2
4 5
1 2
4 1
样例输出
YES
NO
解析:输入的是两个整数为一对每个整数表示节点编号,同时这两个节点之间有一条边存在,且是由第一个节点指向第二个节点,英文的大致内容就是去判定输入的内容是否能组成一颗树。
首先直接输入0 0表明此时是一颗空树,应该输出is a tree.
当出现节点自己到自己之间的边时,应该输出 is not a tree.
如果输入的边与节点的数量不满足edge=vex-1的话也是不属于树的
另外当输入的树中出现环的话也是不属于树
以上这些情况都要考虑到。
其实自己也是看了其他人的博客自己写的代码。
开始本人是这样考虑的,要想组成一颗树,满足了节点和边的条件外,只要输入的节点中只有一个节点的入度是0,其他的节点的入度是零就OK了(有同学跟我一样这么想么?)
如果按照上面这样想的话我画个草图就明白自己为什么wa了哈
上面这个草图满足节点和边的条件,也满足入度出度的情况,但是很明显这不是一颗树。
应该是用到并查集的知识(自己对这点还没有深刻的理解)
除了考虑空树和节点与边之间的关系外,还要得到输入每条边两个节点的最顶上的父亲节点也就是并查集中的Find函数,为什么要这么做类?因为如果x,y两个节点的父节点相同则肯定不是树,因为环就是这种情况。同时还要检查y节点的父节点是不是本身,也就是说x指向的这个节点(y)是否已经有节点指向了它,如果有节点指向了它,此时再有节点指向它则违反了树中入度大于1的规则,可见并查集是多么的有用有效。
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int maxn=1009;
int t;
int n,m;
int p[maxn];
int vis[maxn];
int find(int x)
{
int r=x;
while(r!=p[r])
r=p[r];
int i=x;
while(i!=r)
{
int j=p[i];
p[i]=r;
i=j;
}
return r;
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
for(int i=0;i<=n;i++)
p[i]=i;
int a,b;
int edge=0,v=0;
int flag=0;
int x,y;
memset(vis,0,sizeof(vis));
while(m--)
{
scanf("%d%d",&a,&b);
if(a==0&&b==0)
printf("YES\n");
if(a==b)
{flag=1;
continue;
}
else
{
if(!vis[a])
{
vis[a]=1;
v++;
}
if(!vis[b])
{
vis[b]=1;
v++;
}
x=find(a);
y=find(b);
//printf("%d %d\n",x,y);
if(x==y||y!=b)
{
flag=1;
continue;
}
/*
判断是否为树
x==y的情况是出现环状指向了同一个父亲节点的情况
,此时组成的边也可能节
点数满足关系,当此时不是树
y!=b的情况是b此时已经有父亲
节点此时又有一个节点指向了b,
也就是树中不存在入度大于一的节点
*/
p[y]=x;
edge++;
}
}
//printf("ss%d %d\n",v,edge);
if(flag||v!=edge+1)
printf("NO\n");
else
printf("YES\n");
}
return 0;
}