描述
题解
这道题方法很多,比较好的方法是直接根据树的性质来判断,先判断是否有环,可以通过边数来判断,n个结点最多有n-1条边,不然一定会有环,接着判断根的个数,也就是入度为0的个数,必须为1,最后判断其他根节点入度是否都为1,否则说明不是树!
当然,判断环的部分也可以用并查集,但是这个并查集需要注意些剪枝优化,不然会TLE的,我一开始直接拿着小希的迷宫的代码改了一行提交,TLE了两次……怪我太天真了。
代码
#include <stdio.h>
const int MAXN = 100010;
typedef struct
{
int num, root, conn; // 数据、根、入度
} Node;
int NodeNum;
Node node[MAXN];
void init()
{
NodeNum = 0;
for (int i = 0; i < MAXN; 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)
{
if (n > NodeNum)
{
NodeNum = n;
}
if (m > NodeNum)
{
NodeNum = m;
}
if (!flag && n != 0 && n != 0)
{
continue; // 已经确定不是树了,就继续循环
}
if (n == 0 && m == 0)
{
int root_num = 0;
for (int j = 1; j <= NodeNum;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[n].num = 1;
node[m].conn++; // 入度增加一
union_set(n, m);
}
}
return 0;
}