题目:
给出一对对的数字a,b,表示从a到b有一条边。判断这是不是一棵树。
- 多case,每个case以0 0 结尾
- 输入以-1 -1结尾
解法:
这道题还是很坑爹的,其实网上给的并查集的代码大部分都是错误的,虽然我也是错的。但是给的数据很水,节点<100.而且没有针对并查集的反例,,所以我们都过了~~先说说哪些不是树吧:
- 有环:无根,没有入度为0的节点
- 森林:多个根
- 同一个节点有多个父亲:入度不为1
上述问题1、2的结合,可能给出的图中,有一个环,然后还有一颗树。
再说说并查集怎么做的吧:
先把准备工作做好
- 每读入一对顶点,看他们是不是在同一个集合,如果是肯定形成环,肯定不是树。
- 用set容器保存节点,最后遍历每个节点,看他们是不是属于同一个集合,如果不是,那么就形成了森林,肯定不是树
- 所以并查集解决了:带有1、2、4的问题,但如果单单是3的问题,那么并查集就没办法处理了。例如:1 2 3 2 0 0,这个case,应该不是树,但并查集判断出来是树。(这里可以用入度,map容器解决)
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
using namespace std;
int par[100+5],ranks[100+5];
void init(){
for(int i = 0; i<100; i++){
par[i] = i;
ranks[i] = 0;
}
}
int find(int x){
if(par[x] == x)
return x;
else
return par[x] = find(par[x]);
}
void unite(int x, int y){
x = find(x);
y = find(y);
if(x == y) return ;
if(ranks[x] < ranks[y])
par[x] = y;
else {
par[y] = x;
if(ranks[x] == ranks[y]) ranks[x]++;
}
}
bool same(int x, int y){
return find(x) == find(y);
}
int main()
{
int cas = 1;
bool over = false;
while(1)
{
int a,b;
bool OK = true;
set<int> s;
init();
while(1)
{
scanf("%d%d",&a,&b);
if(a == b && a == 0) break;
if(a == b && a == -1){
over = true;
break;
}
s.insert(a);
s.insert(b);
if(!same(a,b) && OK) unite(a,b);
else{
OK = false;
continue;
}
}
if(over) break;
set<int>::iterator ite;
ite = s.begin();
int root = find(*ite);
for(; ite!=s.end(); ite++){
if(root != find(*ite)) OK = false;
}
if(OK) printf("Case %d is a tree.\n",cas);
else printf("Case %d is not a tree.\n",cas);
cas++;
}
return 0;
}