POJ1182 食物链 并查集

来源:互联网 发布:java中什么是重写 编辑:程序博客网 时间:2024/05/01 20:31

并查集高级应用

中文题,题目大意就不多说了。

两种解决方法:

第一种:

这种方法我也是看了这个帖子才知道的:http://blog.csdn.net/c0de4fun/article/details/7318642/

怎么说呢,本题主要是要考虑到并查集的权值,我们假设par[ a ]=b,即b是a的父亲,我们用rank[ a ]=0来表示a和b是同一类,rank[ a ]=1来表示b可以吃a,rank[ a ]=2表示a可以吃b,为什么这么定义那个帖子已经解释的很明白了。

然后是对于每一组输入的数据d  a  b,我们需要判断他的真假,后两条都很容易判断,主要是判断第一条:判断当前的话是否与前面的真话相冲突。这很好判断,若a和b的根结点相同,那么如果(rank[y]-rank[x]+3)%3和d-1相等的话,这句话就是真话,否则为假话;若a和b的根结点不相同,说明两者不在同一集合里,我们需要先把他们放到同一个集合里,然后再进行判断。这地方还需要自己再推导一下rank的值。

#include <cstdio>#include <cstring>using namespace std;#define MAX 50010int par[MAX],rank[MAX];void Init(int n){    for(int i=1;i<=n;i++)    {        par[i]=i;        rank[i]=0;    }}int Find(int x){    if(x!=par[x])    {        int px=Find(par[x]);        rank[x]=(rank[x]+rank[par[x]])%3;        par[x]=px;    }    return par[x];}bool Union(int x,int y,int d){    int px,py;    px=Find(x);    py=Find(y);    if(px==py)      if((rank[y]-rank[x]+3)%3==d) return true;      else return false;    par[py]=px;    rank[py]=(rank[x]-rank[y]+d+3)%3;    return true;}int main(){    int n,k,a,b,d,i;    scanf("%d%d",&n,&k);    Init(n);    int sum=0;    for(i=1;i<=k;i++)    {        scanf("%d%d%d",&d,&a,&b);        if(a>n||b>n||(a==b&&d==2)) sum++;        else if(!Union(a,b,d-1)) sum++;    }    printf("%d\n",sum);    return 0;}


 第二种方法是从一个学长那里得知的,比上面的容易理解,也没必要推导那些公式。

对于每只动物i,我们创建3个元素 i-A, i-B,i-C,并用这3*N个元素建立并查集,这个并查集维护如下信息:
(1)i-x表示“i属于种类x”。
(2)并查集里的每一个组表示组内所有元素代表的情况都同时发生或不发生。

例如,如果i-A和j-B在同一个组里,就表示如果i属于种类A那么j一定属于种类B,如果j属于种类B那么i一定属于种类A。因此,对于每一条信息,只需要按照下面进行操作就可以了。

·第一种,x和y属于同一种类····合并x-A和y-A,x-B和y-B,x-C和y-C。
·第二种,x吃y················合并 x-A和y-B,x-B和y-C,x-C和y-A。

不过在合并之前,需要先判断合并是否会产生矛盾。例如在第一种信息的情况下,需要检查比如x-A和y-B或者y-C是否在同一组等信息。

#include <cstdio>#include <iostream>using namespace std;#define N 50005int par[3*N],rank[3*N];void Init(int n){    for(int i=1;i<=n;i++)    {        par[i]=i;        rank[i]=1;    }}int Find(int x){    if(x!=par[x])      return par[x]=Find(par[x]);    return x;}void Union(int x,int y){    x=Find(x);    y=Find(y);    if(x==y) return ;    if(rank[x]>rank[y])    {        par[y]=x;        rank[x]+=rank[y];    }    else    {        par[x]=y;        rank[y]+=rank[x];    }}bool Same(int x,int y){    return Find(x)==Find(y);}int main(){    int n,k,i;    int d,a,b;    int ans=0;    scanf("%d%d",&n,&k);    Init(3*n);    for(i=1;i<=k;i++)    {        scanf("%d%d%d",&d,&a,&b);        a-=1;        b-=1;//把a和b变成0到n-1的范围;        if(a>=n||b>=n)        {            ans++;            continue;        }        if(d==1)//a和b属于同一类        {            if(Same(a,b+n)||Same(a,b+2*n)) ans++;            else            {                Union(a,b);                Union(a+n,b+n);                Union(a+2*n,b+2*n);            }        }        else//a吃b        {            if(Same(a,b)||Same(a,b+2*n)) ans++;            else            {                Union(a,b+n);                Union(a+n,b+2*n);                Union(a+2*n,b);            }        }    }    printf("%d\n",ans);    return 0;}


 

 

 

0 0
原创粉丝点击