POJ 1182 食物链 并查集

题目链接:http://acm.hust.edu.cn/vjudge/contest/view.action?cid=66964#problem/E

题意:求假话的数量。

怎么说呢?题目就是会了不难,难的不会!!在网上了看了N多题解,绝大多数都是用一个什么公式来更新的,感觉那个真的不太适合我,说以也就没管了。偶然在白神的一本上看到了此题的另一种解法,可以说是通俗易懂,nice!上正文:

思路:维护每一个动物的三种关系:自己同类的,吃自己的,被自己吃的。如果用三个数组来来维护,即:A表示同类,B表示被自己吃的,C表示吃自己的,那么这样一来,就不能用同一个pre数组来维护了,因为A[i],B[i],C[i]这三个集合的意义是不一样的,但是pre[i] 却只有一种意思。怎么办呢?其实很简单,为了解决pre的重复问题,我们开一个三倍于N一维数组就行了。其中1~N表示同类,N+1~N+N表示1~N对应的动物所吃,2N+1~2N+N表示吃1~N对应的动物,这样一来,我们就可以用一个pre数组来维护三者之间的关系了。

此题还得注意的是:只有一组数据输入,否则会WA。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#define maxn 100010
using namespace std;

int pre[maxn*3];
int N,K;

int find(int x){
    int t=x;
    while(pre[t]!=t) t=pre[t];
    while(x!=t) pre[x]=t,x=pre[x];
    return t;
}

int judge(int a,int b,int c){
    if(a==2 && b==c) {return 0;}
    if(b>N || c>N) {return 0;}
    int fb=find(b),fc=find(c);
    if(a==1){
        if(fb==find(c+N) || fb==find(c+2*N)){ return 0;}
        pre[fb]=fc;
        pre[find(b+N)]=find(c+N);
        pre[find(b+2*N)]=find(c+2*N);
        return 1;
    }
    if(fb==fc || fb==find(c+N)) {return 0;}
    pre[fb]=find(c+2*N);
    pre[find(b+N)]=fc;
    pre[find(b+2*N)]=find(c+N);
    return 1;
}

int main(){
    //freopen("D:in.txt","r",stdin);
    cin>>N>>K;
    int a,b,c,ans=0;
    for(int i=0;i<=3*N;i++) pre[i]=i;
    for(int i=0;i<K;i++){
        scanf("%d %d %d",&a,&b,&c);
        if(judge(a,b,c)) continue;
        ans++;
    }
    cout<<ans<<endl;
    return 0;
}


### 并查集算法的时间复杂度分析 并查集是一种高效的用于处理集合合并与查询的算法。在POJ 1182 食物链问题中,使用了并查集来判断动物之间的关系,并且通过路径压缩和按秩合并等优化手段,可以极大地提高算法的效率。 #### 路径压缩的影响 路径压缩是并查集中一种重要的优化技术,它能够将查找过程中经过的所有节点直接连接到根节点上。这种操作使得后续查找的时间复杂度接近于常数[^1]。具体来说,路径压缩后的查找操作时间复杂度可以用阿克曼函数的反函数 \( \alpha(n) \) 来表示,其中 \( n \) 是集合中的元素个数。阿克曼函数的增长速度极慢,因此 \( \alpha(n) \) 在实际应用中几乎可以视为常数。 ```python def Find(x): if x != par[x]: par[x] = Find(par[x]) # 路径压缩 return par[x] ``` #### 按秩合并的作用 按秩合并是一种优化策略,它通过将较小的树合并到较大的树上来减少树的高度。这种方法结合路径压缩后,可以进一步降低操作的时间复杂度[^2]。在实际实现中,可以通过维护一个数组 `rank` 来记录每个集合的深度,并在合并时选择深度较小的树挂接到深度较大的树上。 ```python def Union(x, y): rootX = Find(x) rootY = Find(y) if rootX != rootY: if rank[rootX] > rank[rootY]: par[rootY] = rootX elif rank[rootX] < rank[rootY]: par[rootX] = rootY else: par[rootY] = rootX rank[rootX] += 1 ``` #### 时间复杂度总结 对于 POJ 1182 食物链问题,假设总共有 \( n \) 个动物和 \( m \) 条关系,则初始化并查集的时间复杂度为 \( O(n) \),每次查找或合并操作的时间复杂度为 \( O(\alpha(n)) \)[^2]。由于 \( \alpha(n) \) 的增长极其缓慢,在实际情况下可以认为其为常数。因此,整个算法的时间复杂度主要由关系数量 \( m \) 决定,最终的时间复杂度为 \( O(m \cdot \alpha(n)) \)[^1]。 ### 代码示例 以下是一个完整的并查集实现,适用于 POJ 1182 食物链问题: ```python class UnionFind: def __init__(self, n): self.par = list(range(3 * n)) self.rank = [0] * (3 * n) def Find(self, x): if self.par[x] != x: self.par[x] = self.Find(self.par[x]) return self.par[x] def Union(self, x, y): rootX = self.Find(x) rootY = self.Find(y) if rootX != rootY: if self.rank[rootX] > self.rank[rootY]: self.par[rootY] = rootX elif self.rank[rootX] < self.rank[rootY]: self.par[rootX] = rootY else: self.par[rootY] = rootX self.rank[rootX] += 1 def Same(self, x, y): return self.Find(x) == self.Find(y) ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值