POJ 1308 Is It A Tree? 并查集求解

题目链接: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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值