并查集POJ1308

题目:

题目链接

  给出一对对的数字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;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值