题目的意思就是输入一组有向边,让我们判断是不是一颗树。
我们知道,如果把树上的有向箭头去掉之后,树其实就是一个无环单连通无向图。所以在输入的时候我们就可以把它当做无向边建立并查集。如果两个点在同一集合内,就是有环,不是树。在并查集建立完了之后再判断有几个连通块,如果有多个,则不是树。那么现在我们把没有方向的树的特性判断完了,那么加上箭头该怎么判断呢?用一个数组表示每个点的入度。一棵树有且只有一个点入度为0,其余点入度为1。如果这些条件都符合,那么输入数据就是一棵树。
另外还要注意因为输入的不是连续的,我们可以用map把这些点映射到1—n。
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <set>
#include <map>
using namespace std;
const int maxn = 100;
int cnt, ok;
int p[maxn], in[maxn], out[maxn];
int find_root(int x){
if (p[x] == x)
return x;
return p[x] = find_root(p[x]);
}
void judge1(){
int a = 0;
for (int i = 1; i <= cnt - 1; i++)
if (p[i] == i)
a++;
if (a != 1){ok = 0; return;}
}
void judge2(){
int a = 0, b = 0;
for (int i = 1; i < cnt; i++){
if (!in[i])
a++;
else if (in[i] != 1)
b++;
}
if (a != 1 || b)
ok = 0;
}
int main()
{
//freopen("1.txt", "r", stdin);
int u, v, Case = 1;
map<int, int> mi;
while (scanf("%d%d", &u, &v), u != -1){
if (!u && !v){
printf("Case %d is a tree.\n", Case++);
continue;
}
for (int i = 1; i <= maxn; i++)
p[i] = i;
ok = 1;
cnt = 1;
mi[u] = cnt++;
mi[v] = cnt++;
out[mi[u]]++;
in[mi[v]]++;
int rx = find_root(mi[u]);
int ry = find_root(mi[v]);
if (rx != ry)
p[ry] = rx;
else ok = 0;
if (u == v)
ok = 0;
while (scanf("%d%d", &u, &v), u && v){
if (u == v)
ok = 0;
if (mi.find(u) == mi.end())
mi[u] = cnt++;
if (mi.find(v) == mi.end())
mi[v] = cnt++;
out[mi[u]]++;
in[mi[v]]++;
int rrx = find_root(mi[u]);
int rry = find_root(mi[v]);
if (rrx != rry)
p[rry] = rrx;
else ok = 0;
}
judge1();
judge2();
if (ok)
printf("Case %d is a tree.\n", Case++);
else printf("Case %d is not a tree.\n", Case++);
mi.clear();
memset(in, 0, sizeof(in));
memset(out, 0, sizeof(out));
}
return 0;
}