并查集POJ1308

来源:互联网 发布:上海短租 知乎 编辑:程序博客网 时间:2024/05/21 17:13

题目:

题目链接

  给出一对对的数字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;}
0 0
原创粉丝点击