Poj-1182

强烈建议初学并查集的同学先去做poj2524 1611找找自信,我刚学完并查集就做了这道并查集中的经典食物链,即使看解析也看了半个多点才看明白,以下是我看的大神思路 写的超级好:

【题解】食物链(Poj 1182)·两种方法的并查集(超级简单,而且非常容易理解)·C/C++_问题 c: 食物链-CSDN博客

大神写的也是十分完善了,我就不过多赘述,仅仅在这里写一下本题的大致思路,加深一下自己的认识。

首先 网上有两种主流的思路

1.使用三倍大小的并查集来储存食物链 将每个动物分为A B C三种情况 但我感觉这种思路比较抽象不如第二种可以让人学到知识(其实是自己没理解

2.设置结构体 内储存当前节点对父节点的状态

0:同类

1:父吃自己

2:自己吃父亲

?为什么 

父亲假设为A +1变成B 父亲吃自己 +2 变成C 自己吃父亲 A-B-C-A这种关系

如果想得到一个节点对另一个节点的关系 就需要遍历当前的树 相加状态

得到结果mod3 利用这种思想可以求任意节点之间的关系

其次我们理解了这种思想就需要在程序当中利用他

并查集一共就两个操作 find union

find:不变

union 时候由于多了状态需要改变:

仅改变插入节点的状态:

同类:

(b-a+3)mod3 b代表被插入树的权值之和 a代表插入树的权值之和

x吃y

需要+1使得y树权值变大 +1 变成x吃y的关系

解释的比较抽象还是看大佬的思路吧

#include <iostream>
using namespace std;
int N, M, RootSum = 0, Tie = 0;
#define MAX_N 50002
struct Node {//设计一个结构,不仅有前驱节点的信息,还有权值。
    int Pre, PreSum;
} Tree[MAX_N];
int FindR(int x) {//查找根节点,并把路径上面的值加上。
    if (x == Tree[x].Pre) {
        return x;
    } else {
        RootSum = (RootSum + Tree[x].PreSum) % 3;
        return FindR(Tree[x].Pre);
    }
}
int FindRoot(int x) {
    RootSum = 0;//怕自己忘记更新RootSum=0,所有写了一个FindRoot函数,这样避免使用FindR忘记更新。
    Tree[x].Pre = FindR(x);//更新前驱节点(父节点)
    Tree[x].PreSum = RootSum;//查找根节点顺便把这条路上面的所有的权值加起来。
    return Tree[x].Pre;
}
bool XEatY(int x, int y) {//判断X是不是吃Y,如果吃就return 1。
    x = Tree[x].PreSum;
    y = Tree[y].PreSum; 
    return ((x == 0 && y == 1) || (x == 1 && y == 2) || (x == 2 && y == 0));
}
int main() {
    scanf("%d %d", &N, &M);
    for (int i = 1; i <= N; i++) Tree[i].Pre = i, Tree[i].PreSum = 0;
    for (int i = 1; i <= M; i++) {
        int D = 0, X = 0, Y = 0;
        scanf("%d %d %d", &D, &X, &Y);
        if (X < 1 || X > N || Y < 1 || Y > N) {
            Tie++;//说谎的次数。
            continue;
        }
        int RootX = FindRoot(X), RootY = FindRoot(Y);//找一下他们的集合,并且在这个过程也把他们挂在了根节点上面
        if (RootX == RootY) {
            if (D == 1) {
                if (Tree[X].PreSum != Tree[Y].PreSum) Tie++;
            } else if (D == 2) {
                if (!XEatY(X, Y)) Tie++;
            }
        } else {  //两个节点之前没有关系,他说什么都是对的。
            if (D == 1) {
                //把y的根节点挂在x的根节点上
                Tree[RootY].Pre = RootX;
                Tree[RootY].PreSum = (Tree[X].PreSum - Tree[Y].PreSum + 3) % 3;//合并的时候重新算一下,他们之间的关系。我这里默认把Y的根节点挂在X的根节点上面。无所谓。您想反过来也可以。您如果看不懂公式是怎么推到的,您可以查阅:“手表原理”。
            } else if (D == 2) {
                //维护关系X,吃Y,还是按照把y的根节点挂在x的根节点,Y的PreSum应该比X的PreSum大一
                Tree[RootY].Pre = RootX;
                Tree[RootY].PreSum = (Tree[X].PreSum + 1 - Tree[Y].PreSum + 3) % 3;
            }
        }
    }
    printf("%d", Tie);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值