洛谷 “P1525 [NOIP2010 提高组] 关押罪犯“ 题解

题目如下

P1525 [NOIP2010 提高组] 关押罪犯
在这里插入图片描述
题型:并查集
貌似还可以用二分图来做,明天做做试试再发个题解.

思路

首先我们想到用并查集解题,并查集模板我之前的博客有写
链接
首先我们看到,这道题相当于是一个不断排除的过程.
先按怨气值从大到小遍历仇恨关系,如果这两个罪犯可以放到不同的监狱,就把两个罪犯分到两个监狱并且继续遍历,如果不能就直接输出两者的怨气值,因为是从大到小遍历,可以保证此时是监狱内冲突影响最大的事件,从而就不用再考虑后面的仇恨关系了.
那我们现在的关键问题就是怎么判断两名罪犯是否可以放在同一个监狱了.
首先我们从怨气值最大的仇恨关系考虑,显然两个人必须放在不同监狱,然后下一组,如果下面组的两名罪犯在之前没有出现过,那么现在这两个也分到不同的监狱,但是我们此时不知道两人分别分配到哪个监狱,这就需要后面的数据了.
这是我们引入一个概念——敌人
必须放在两个不同监狱的两名罪犯视为敌人.
那么,敌人的敌人,是不是就和自己处在同一个监狱了.
之后在遍历过程中,先判断两种的敌人,发现如果对面敌人和自己在不同监狱,那么两个人就是敌人的敌人了,那就代表他们被分配在同一个监狱,就输出怨气值,程序结束.
如果不满足,再看如果自己没敌人,那么对面就是自己敌人了.如果自己有敌人,那么自己敌人就和对面一个监狱了.

所以写得如下程序

上ac代码

#include<bits/stdc++.h>
using namespace std;

int n,m,enemy[20010],father[20010],rank_[20010];

struct line_{//写成结构体比较方便排序
    int x,y,v;
}line[200010];

bool cmp(line_ a,line_ b){
    return a.v>b.v;
}

void init(int n){//初始化结构体
    for(int i=1;i<=n;i++){
        father[i]=i;
        rank_[i]=1;
    }
}

int find_(int x){
    return x==father[x]?x:(father[x]=find_(father[x]));
}

void merge_(int i,int j){//按秩排序,可以减少搜索速度,但是其实可以直接排.
    int x=find_(i),y=find_(j);
    if(rank_[x]<=rank_[y]) father[x]=y;
    else father[y]=x;
    if(rank_[x]==rank_[y]&&x!=y) rank_[y]++;
}

int main(){
    int x,y,w;
    cin>>n>>m;
    init(n);
    
    for(int i=0;i<m;i++){
        scanf("%d%d%d",&line[i].x,&line[i].y,&line[i].v);
    }

    sort(line,line+m,cmp);//对仇恨关系按怨气从大到小排序

    for(int i=0;i<m;i++){
        if(find_(line[i].x)==find_(line[i].y)){
            cout<<line[i].v<<endl;
            return 0;
            //如果发现两个其实已经在之前分配在同一监狱了
            //就输入怨恨值并结束程序.
        }
        //所以,如果上面不满足,两个犯人就分配到不同监狱了
        if(!enemy[line[i].x]) enemy[line[i].x]=line[i].y;
        else merge_(enemy[line[i].x],line[i].y);
        //如果第一个犯人没敌人,第二个就成了第一个犯人的敌人
        
        //如果第一个犯人有敌人
       	//那么把第二个犯人合并到第一个犯人敌人的并查集
        //即把第二个犯人放入第一个犯人敌人的监狱
        if(!enemy[line[i].y]) enemy[line[i].y]=line[i].x;
        else merge_(enemy[line[i].y],line[i].x);
        //同上
    }
    cout<<0<<endl;//如果上面始终都没找到有仇恨关系并且在同一监狱的犯人,就输出0
    return 0;
}

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值