poj2492(并查集---类似“食物链”)

本文介绍了一种通过并查集算法解决食物链中物种间关系判断的问题,利用不同类的合并树更新关系,确保能够快速判断两个物种是否为同一属性。

http://poj.org/problem?id=2492(poj 2492 点击打开题目链接)

思路:不同类的合并树,更新关系。

分析:和poj1703很类似,都是“食物链”的简化版,这个题没出来的原因还是没理解透。如果属于同一颗树,说明可以判断两者之间的关系,但是不能说明是同性,只有既属于同一棵树,又关系相同,才能说明是同性,在输入的过程中就可以判断了,而不需要把所有的都存下来再遍历判断(但是这里不是很懂啊)。

注意:这种输入的两个是不同类的,感觉可以套模板了。

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <cstring>

using namespace std;

const int maxn = 2000 + 10;
int father[maxn];
int rank[maxn];

int n,m;

void init()
{
    for(int i = 0; i <= n; i++)
    {
        father[i] = i;
    }

    memset(rank,0,sizeof(rank));
}


int find(int x)
{
    if(x == father[x])
        return x;
   //这个地方注意,自己写的时候没写出来
    int y = father[x];//y是x的父亲,y是父节点,不是根节点
    father[x] = find(y);
    rank[x] = (rank[x] + rank[y]) % 2;//这里可以记住,以后直接用
    return father[x];
}

void unin(int a,int b)
{
    int fa = find(a);
    int fb = find(b);

    father[fa] = fb;
    //记住直接用
    rank[fa] = (rank[a] + 1 + rank[b]) % 2;

}


int main()
{
    int t,a,b,ans = 0;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        init();
        bool isok = true;
        while(m--)
        {
            scanf("%d%d",&a,&b);
            if(find(a) == find(b))
            {//属于同一颗树,并且关系相同
                if(rank[a] == rank[b])
                {
                    isok = false;
                }

            }
            else if(find(a) != find(b))
            {
                unin(a,b);
            }

        }



        printf("Scenario #%d:\n",++ans);

        if(!isok)
            printf("Suspicious bugs found!\n");
        else
            printf("No suspicious bugs found!\n");
        if(t)
            printf("\n");
    }
}
  • 所有的都要处理,但是在处理过程中就可以判断是否存在关系相同的
### POJ 1182 食物链 C++ 解法 POJ 1182 食物链问题可以通过并查集(Union-Find)算法来解决。该问题的核心在于通过带权并查集维护不同动物之间的关系,并判断输入的关系是否与已有关系矛盾。 以下是一个完整的 C++ 实现代码,基于带权并查集的解法: ```cpp #include <iostream> #include <stdio.h> using namespace std; const int MAXN = 50010; int pre[MAXN], offset[MAXN]; int n, m; // 初始化并查集 void make_set(int n) { for (int i = 0; i <= n; i++) { pre[i] = i; offset[i] = 0; // 自己与自己同类,所以偏移为0 } } // 查找根节点,并更新路径上的偏移量 int find_set(int x) { if (x == pre[x]) return x; int fx = find_set(pre[x]); offset[x] = (offset[x] + offset[pre[x]]) % 3; // 状态转移方程,important return pre[x] = fx; } // 合并两个集合,并更新偏移量 void union_set(int x, int y, int t) { int fx = find_set(x); int fy = find_set(y); if (fx != fy) { pre[fy] = fx; offset[fy] = (offset[x] + t - 1 - offset[y] + 3) % 3; // 更新偏移量 } } int main() { cin >> n >> m; int d, x, y; int ans = 0; make_set(n); for (int i = 0; i < m; i++) { scanf("%d%d%d", &d, &x, &y); if (x > n || y > n || (x == y && d == 2)) { // 检查非法输入 ans++; continue; } if (find_set(x) == find_set(y)) { // 如果两个动物在同一集合中 if ((offset[y] - offset[x] + 3) % 3 != d - 1) { // 判断关系是否矛盾 ans++; } } else { union_set(x, y, d); // 合并两个集合 } } cout << ans << endl; return 0; } ``` #### 代码解析 1. **初始化**:`make_set` 函数用于初始化每个节点的父节点为自己,并将偏移量设置为 0[^1]。 2. **查找根节点**:`find_set` 函数通过路径压缩优化查找根节点,并在查找过程中更新路径上的偏移量[^1]。 3. **合并操作**:`union_set` 函数用于合并两个集合,并根据输入的关系类型更新偏移量[^1]。 4. **输入处理**:对于每组输入 `(d, x, y)`,首先检查是否为非法输入,然后判断 `x` 和 `y` 是否在同一集合中。如果在同一集合中,则检查关系是否矛盾;否则,合并两个集合[^1]。 #### 注意事项 - 偏移量的计算需要考虑模 3 的操作,因为关系类型只有三种:同类、捕食和被捕食[^1]。 - 路径压缩和按秩合并可以进一步优化并查集的性能,但在此题中未实现按秩合并[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值