题目:已知有m条边连接顶点,判断其是否为一棵树
思路:树的条件:
要么是空树,要么满足连通无环
1.无环(可通过并查集判断)
2.连通(不能出现森林,即所有输入的点必须只在一个集合里边)
要么是空树,要么满足连通无环
1.无环(可通过并查集判断)
2.连通(不能出现森林,即所有输入的点必须只在一个集合里边)
自己wa了好几遍。
总结下来有几个原因:
1.空树特判
2.输入问题,无论是不是树,都必须输入完数据才行。
3.初始化一定要放在最前边
4.连通无环是并列关系,但是只要一个不满足,在输入完成的情况下,均可以直接输出结果,算是一点优化吧。
总结下来有几个原因:
1.空树特判
2.输入问题,无论是不是树,都必须输入完数据才行。
3.初始化一定要放在最前边
4.连通无环是并列关系,但是只要一个不满足,在输入完成的情况下,均可以直接输出结果,算是一点优化吧。
在网上摘别人的一些易错用例:
1: 0 0 空树是一棵树
2: 1 1 0 0 不是树 不能自己指向自己
3: 1 2 1 2 0 0 不是树....自己开始一直在这么WA 好郁闷 重复都不行呀~~5555
4: 1 2 2 3 4 5 不是树 森林不算是树(主要是注意自己)
5: 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 1 注意 一个节点在指向自己的父亲或祖先 都是错误的 即 9-->1 错
6: 1 2 2 1 0 0 也是错误的
#include <cstdio>
#include <algorithm>
#include <cstring>
#define N 105
using namespace std;
int f[N];
int sign[N];//记录出现过的顶点
void init()
{
for (int i = 1; i <= N; i++)
f[i] = i;
}
int find(int x)
{
return x == f[x] ? x : f[x] = find(f[x]);
}
void merge(int x, int y)
{
int t1 = find(x), t2 = find(y);
if (t1 != t2) f[t2] = t1;
}
int same(int x, int y)
{
return find(x) == find(y);
}
int main()
{
int x, y,cas = 0;
while (~scanf("%d%d", &x, &y) && (x != -1 || y != -1))
{
if (x == 0 && y == 0)//输入点只有0,0。也就是说是空树特判
{
printf("Case %d is a tree.\n", ++cas);
continue;
}
//初始化
init();
memset(sign, 0, sizeof(sign));
int ans = 1;//假定是树
//标记输入顶点
sign[x] = sign[y] = 1;
if (x == y)//出现自环
ans = 0;
else//合并前两个元素
merge(x, y);
//先一次性输入完
while (~scanf("%d%d", &x, &y) && (x || y))
{
sign[x] = sign[y] = 1;
if (same(x, y))
ans = 0;
else
merge(x, y);
}
if (ans == 0)//出现环
printf("Case %d is not a tree.\n", ++cas);
else//下边判断连通性
{
int cnt = 0;
for (int i = 1; i <= N; i++)
{
if (sign[i] && f[i] == i)//如果该点被标记(即出现过)并且是树根,连通分量的个数加一
cnt++;
}
if (cnt > 1)//集合的连通分量大于1,即为森林
printf("Case %d is not a tree.\n", ++cas);
else//满足连通无环,说明是树
printf("Case %d is a tree.\n", ++cas);
}
}
return 0;
}