POJ 1308 Is It A Tree?
题目链接:vjudge传送门
题目大意:
给定若干条有向边,判断所给的所有边是否能组成一颗树(包含空树)
具体思路:
离散数学图论知识,只有一个结点入度为0,其它结点入度均为1的图是树,保存每个点的入度,最后判断即可
也可按树的定义来,用并查集来维护
新加入的边,被指向的结点必须是并查集中的祖先结点(排除入度大于1和环的情况),加入完后把两点合并
处理完后判断是否为空树或者森林即可。
下方分别贴出两种方式的代码
具体代码:
//保存入度
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 1e4;
int fa[N], visit[N],degree[N];
void init()
{
memset(visit, 0, sizeof(visit));
memset(degree, 0, sizeof(degree));
for (int i = 1; i <= N; i++)
fa[i] = i;
}
int main()
{
int cnt = 1, flag = 1;
int n, m;
init();
while (~scanf("%d%d", &n, &m))
{
if (m == -1 && n == -1)break;
if (!(m == 0 && n == 0))
{
visit[m] = 1, visit[n] = 1;
degree[m]++;
}
else //判断空树或森林的情况
{
int first = 1, f;
int isNull = 1, isForest = 0;
for (int i = 1; i <= N; i++)if (visit[i])isNull = 0; //考虑空树的情况
if (!isNull) { //不是空树则判断是否为森林
int zeroDeg = 0; //入度为0的点
for (int i = 1; i <= N; i++)
{
if (visit[i]) {
if (degree[i] == 0)zeroDeg++; //记录入度为0的点的个数
if (degree[i] > 1) { //每个点的入度都小于2
isForest = 1;
break;
}
}
}
if (zeroDeg != 1)isForest = 1; //树只有一个入度为0的点
}
if (isNull)flag = 1; //空树
if (isForest)flag = 0; //森林
if (flag) printf("Case %d is a tree.\n", cnt++);
else printf("Case %d is not a tree.\n", cnt++);
init();
flag = 1;
}
}
return 0;
}
//并查集
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 1e6;
int fa[N], visit[N];
void init()
{
memset(visit, 0, sizeof(visit));
for (int i = 1; i <= N; i++)
fa[i] = i;
}
int find(int son)
{
if (son == fa[son])
return son;
return fa[son] = find(fa[son]);
}
void unite(int u, int v)
{
int r1 = find(u);
int r2 = find(v);
if (r1 == r2) return;
fa[r2] = r1;
}
int main()
{
int cnt = 1, flag = 1;
int n, m;
init();
while (~scanf("%d%d", &n, &m))
{
if (m == -1 && n == -1)break;
if (!(m == 0 && n == 0))
{
visit[m] = 1, visit[n] = 1;
if (fa[m] == fa[n])flag = 0; //新加入的边,from->to,to所属集合不能和from相同
else if (fa[m] != m)flag = 0; //新加入的边,被指向的必须是祖先结点
else unite(n, m);
}
else
{
//判断空树或森林的情况
int root = 0;
int isNull = 1, isForest = 0;;
for (int i = 1; i <= N; i++) {
if (visit[i] && fa[i]==i) {
root++;
isNull = 0;
if (root > 1) {
isForest = 1;
break;
}
}
}
if (isNull)flag = 1; //空树
if (isForest)flag = 0; //森林
if (flag) printf("Case %d is a tree.\n", cnt++);
else printf("Case %d is not a tree.\n", cnt++);
init();
flag = 1;
/*
之前一直WA的代码,原来是fa[i]并不是总等于find(i),
搞不懂,要是路径压缩算法正确,同一个集合的点不都是指向自己的祖先结点吗
//判断空树或森林的情况
int first = 1, f;
int isNull = 1;
for (int i = 1; i <= N; i++) {
if (visit[i]) {
isNull = 0;
if (first) {
f = find(i); //一开始这里是写fa[i],WA了无数次
first = 0;
}
else if (f != find(i)) { //一开始这里是写fa[i]
flag = 0; //森林
break;
}
}
}
if (isNull)flag = 1; //空树
if (flag) printf("Case %d is a tree.\n", cnt++);
else printf("Case %d is not a tree.\n", cnt++);
*/
}
}
return 0;
}