食物链(POJ 1182)

Sample Input

100 7
1 101 1 
2 1 2
2 2 3 
2 3 3 
1 1 3 
2 3 1 
1 5 5

Sample Output

3

前几天做题的时候,做到了种类并查集,于是决定把以前学的“食物链”也解决掉,这两种题都是普通并查集的进阶,做起来挺有意思的。

https://blog.csdn.net/niushuai666/article/details/6981689这篇博客将的特别详细,但看的过程中遇到了一些问题,所以写一下自己的思路。

与普通的并查集相比,需要开一个rela[maxn]数组来保存与父亲结点的关系。

  就此题而言生物间共存在三种关系:

  rela[x] = 0   ……表示节点x与其父节点rootx的关系是:同类
  rela[x] = 1   ……表示节点x与其父节点rootx的关系是:被根结点吃
  rela[x] = 2   ……表示节点x与其父节点rootx的关系是:吃根结点

 (因为生物间共存在三种关系,而D只有两种状态,无法表示出生物间的关系,所以要自己找出一组数与D有联系的数)

此处规定关系为 D = D-1(D为输入的关系),(可能你会认为这样操作会使关系改变,那么请看下面蓝体字(前提是懂得了并查集中内在偏移量的关系,如果不懂请看链接))

并查集在查找过程中要进行数据压缩,父亲结点会改变!!而rela[x]是x与父结点的关系,所以要更新!!!

 

关系更新要进行两次:

1.原来没有进行合并过的个体。(图一)

2.数据压缩(图二)

                                                  图一

                           图二

 

tx       ty

|          |

x   ~    y

 rootx->rooty =  rootx->x + x->y + y->rooty

进一步转化:rootx->rooty = ( relation[x]+ (d-1) +(3-relation[y]) ) %3 = relation[rooty]],(模3是保证偏移量取值始终在[0,2]间)

解释

1.题目原意 D = 2表示x吃y , 而在我们规定的关系中(D = D-1)1表示 x被y吃,则 (题目意思)x -> tx = tx -> x(自定义规 则) (x吃tx,与tx被x吃一个意思),所以rootx -> x = relation[x]。

2. root x-> rooty = 3-relation[rooty]

#include<iostream>
#include<cstdio>
#include<algorithm>
const int maxn = 50000+5;
using namespace std;
int N,K;
int D,X,Y,ans;
int parent[maxn];
int rela[maxn];     
int rx,ry;
void init()
{
    for(int i = 1;i <= N;i++){
        parent[i] = i;
        rela[i] = 0;
    }
}

int getroot(int a){
    if(parent[a] == a)
        return a;
    else{
        int temp = parent[a];   //递归由最小元素组成思考
        parent[a] = getroot(temp);
        rela[a] = (rela[a]+rela[temp])%3;
        return parent[a];
    }
}
int main()
{
    scanf("%d %d",&N,&K);
    init();
    ans = 0;
    for(int i = 0;i < K;i++){
        scanf("%d%d%d",&D,&X,&Y);  //目的:找出有多少个错误输入
        if(X>N || Y>N){
            ans++;
            continue;
        }
        if(D==2 && X==Y){
            ans++;
            continue;
        }
        rx = getroot(X);
        ry = getroot(Y);
        if(rx != ry){
            parent[rx] = ry;
            rela[rx] = (3-(rela[X]+(D-1)+(3-rela[Y]))%3);
         // parent[ry] = rx;
        //rela[ry] = (3+(D-1)+rela[X]-rela[Y])%3;

        }
        else{
            if(D == 1&& rela[X]!=rela[Y]){
                ans++;
                continue;
            }
            if(D == 2&&(3-rela[X]+rela[Y])%3 != D-1){
                ans++;
                continue;
            }
         }

    }
    printf("%d\n",ans);
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值