题目链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=66964#problem/N
题意:给定一些箭头,判断是否能够形成一棵树。
思路:主要是判断度和环。这题出在并查集专题,说明可以用并查集求解。和Kruskal求最小生成树一样,这里主要用并查集来判断环的情况。但是还需要一些额外入度判断。也就是说,每次加入边a,b时,b只有根节点才有效,此外,a,b还不能为同一个集合。其实,我们可以不用并查集求解,要判断环,我们只要知道该点是否有出度就可以了,只要b有出度,那么加入a,b就会形成环,最后,判断根节点是否为1就行了。
并查集:
#include<iostream>
#include<cstdio>
using namespace std;
int pre[1010],vis[1010];
int find(int x){
int t=x;
while(t!=pre[t]) t=pre[t];
while(x!=t) pre[x]=t,x=pre[x];
return t;
}
int main(){
//freopen("D:\\in.txt","r",stdin);
int a,b,kase=0;
while(cin>>a){
cin>>b;if(a==-1 && b==-1) break;
kase++;
if(!a && !b) {printf("Case %d is a tree.\n",kase);continue;}//特判
for(int i=0;i<1010;i++) pre[i]=i,vis[i]=0;
int op=1;
if(a==b) op=0;
pre[find(b)]=find(a);
vis[a]=vis[b]=1;//记录出现过的数
while(scanf("%d %d",&a,&b)==2){
if(!a && !b) break;
int fa=find(a),fb=find(b);
//这里注意或运算,fb=b时才执行fa==fb时
if(fb!=b || fa==fb) op=0;//只有b为根节点时才有效以及判断环
if(op) pre[fb]=fa,vis[a]=vis[b]=1;
}
int cnt=0;
for(int i=1;i<1010 && op;i++) if(vis[i]){//找rootd的数量
if(find(i)==i) cnt++;
if(cnt>1) op=0;
}
if(op) printf("Case %d is a tree.\n",kase);
else printf("Case %d is not a tree.\n",kase);
}
return 0;
}
非并查集:
#include<iostream>
#include<cstdio>
using namespace std;
int deg[1010],vis[1010];
int main(){
//freopen("D:\\in.txt","r",stdin);
int a,b,kase=0;
while(cin>>a){
cin>>b;if(a==-1 && b==-1) break;
kase++;
for(int i=0;i<1010;i++) deg[i]=vis[i]=0;
if(!a && !b) {printf("Case %d is a tree.\n",kase);continue;}//特判
vis[a]=vis[b]=1;deg[b]++;
int op=1;
if(a==b) op=0;//形成自环
while(scanf("%d %d",&a,&b)==2){
if(!a && !b) break;
if(deg[b] || a==b) op=0;//入度大于1或是形成自环
else if(op) deg[b]++;
vis[a]=vis[b]=1;//标志输入的数
}
int cnt=0;
if(op) for(int i=1;i<1010;i++) if(vis[i]){
if(!deg[i]) cnt++;
if(cnt>1) {op=0;break;}
}
//这里&& cnt必须要,排除都为环时的特殊情况
if(op && cnt) printf("Case %d is a tree.\n",kase);
else printf("Case %d is not a tree.\n",kase);
}
return 0;
}