博客 > 数据结构&算法 > 题目&比赛
# HDU-1325 简单图论(树的性质) ## [HDU-1325](http://acm.hdu.edu.cn/showproblem.php?pid=1325) ## 分析 根据树的性质(其实题目也说了), **一个树里一个节点入度为0(为根), 其他节点入度都为1.** 即这样的情况不是一棵树: ![f06fb107d625eded57b2f38c6d746942.png](/resources/e65b36bb899a48f58d632386af78f92d) 进一步思考有没有满足这一条件的反例呢? 当然有了: ![19c532d15b69a7f485b12f9e44a5e75e.png](/resources/aa7cbc0d6dc342f8b2fcfabac0ac78a4) > 然而, 此题数据实在太薄. 根本没有考虑这种情况的也能AC... 因此这里写的是除了能A题之外更加健壮的方法. 解决方法是: **使用并查集判断整个图是否全连通(无向图形式).** 另外并查集是可以用来判环(无用边)的, 网上题解很多是这样写的. 但其实在这里, 根据树的定义, **判断节点入度(0\*1+1\*n) + 判断全连通**, 这已经是无环的充分条件了. ## 代码 注意处理输入数据会比较繁琐. ```cpp // -------------- GLOBAL ---------------- #define makeNode \ maxn = max(maxn, max(m, n)); \ exist[n] = exist[m] = true; \ ++in[m]; bool exist[10086]; // 节点不一定连续 int in[10086]; // 记录入度 int setTree[10086]; int maxn = -INF; bool flag; // -------------- FUNC ---------------- int find(int x) { return x == setTree[x] ? x : setTree[x] = find(setTree[x]); } // -------------- MAIN ---------------- int main() { ios_base::sync_with_stdio(false); int cnt = 0; int n, m; // 从1开始 while (~INI(n) && ~INI(m) && n >= 0 && m >= 0) { flag = true; // 默认为true(单节点树或空树) if (n || m) { // 不是空树 maxn = -INF; fill0(in); fill0(exist); F0(i, 10086) { setTree[i] = i; } makeNode; setTree[n] = m; // 读入数据 while (~INI(n) && ~INI(m) && n && m) { // 判断条件已经充分了, 没必要判环 // 如果加了这句反而会导致接下来判断全连通出问题 // if (!flag) continue; makeNode; int a = find(n); int b = find(m); if (a == b) { // 出现无用边 // flag = false; continue; } setTree[a] = b; } // if (flag) { bool hasIn0 = false; int initSet = find(maxn); F1(i, maxn) { if (exist[i] && !in[i]) { // 入度为0的节点有且只能有一个(根) if (hasIn0) { // 多了 flag = false; break; } hasIn0 = true; // 正好 } if (exist[i] && in[i] > 1) { // 入度>1的节点不能有 flag = false; break; } if (exist[i] && find(i) != initSet) { // 不全连通可不行啊! flag = false; break; } } if (!hasIn0) { // 少了 flag = false; } // } } // 输出结果 printf("Case %d is ", ++cnt); if (flag) printf("a tree.\n"); else printf("not a tree.\n"); } return 0; } ```