关押罪犯-并查集、贪心

题目来源:Acwing 257.关押罪犯&洛谷 P1525 [NOIP2010 提高组] 关押罪犯
思路来源:这里

题目描述

S 城现有两座监狱,一共关押着 N 名罪犯,编号分别为1~N。
他们之间的关系自然也极不和谐。
很多罪犯之间甚至积怨已久,如果客观条件具备则随时可能爆发冲突。
我们用“怨气值”(一个正整数值)来表示某两名罪犯之间的仇恨程度,怨气值越大,则这两名罪犯之间的积怨越多。
如果两名怨气值为 c 的罪犯被关押在同一监狱,他们俩之间会发生摩擦,并造成影响力为 c 的冲突事件。
每年年末,警察局会将本年内监狱中的所有冲突事件按影响力从大到小排成一个列表,然后上报到 S 城 Z 市长那里。
公务繁忙的 Z 市长只会去看列表中的第一个事件的影响力,如果影响很坏,他就会考虑撤换警察局长。
在详细考察了 N 名罪犯间的矛盾关系后,警察局长觉得压力巨大。
他准备将罪犯们在两座监狱内重新分配,以求产生的冲突事件影响力都较小,从而保住自己的乌纱帽。
假设只要处于同一监狱内的某两个罪犯间有仇恨,那么他们一定会在每年的某个时候发生摩擦。那么,应如何分配罪犯,才能使 Z 市长看到的那个冲突事件的影响力最小?这个最小值是多少?

输入描述

第一行为两个正整数 N 和 M,分别表示罪犯的数目以及存在仇恨的罪犯对数。
接下来的 M 行每行为三个正整数aj,bj,cj,表示aj号和bj号罪犯之间存在仇恨,其怨气值为cj
数据保证1≤aj<bj<N,0<cj≤109 且每对罪犯组合只出现一次。

输出描述

输出共1行,为 Z 市长看到的那个冲突事件的影响力。
如果本年内监狱中未发生任何冲突事件,请输出0。

数据范围

N ≤ 20000 , M ≤ 100000

样例输入

4 6
1 4 2534
2 3 3512
1 2 28351
1 3 6618
2 4 1805
3 4 12884

样例输出

3512

OP

y总给的方法是二分+染色法判断二分图,这个我不太会。可以参考这篇题解

思路

这道题所求即是最大影响值最小的分配方案中,最大的影响值。

想让最大影响值最小,我们便需要首先让仇恨值最大的两名罪犯优先分配到不同监狱,再分配次大的,以此类推,直到某对罪犯无法分配。

那么我们应该如何标记对监狱的分配呢?

先大概说一下并查集补集,这道题中,如果 f a t h e r [ x ] = y + n father[x]=y+n father[x]=y+n 则说明x与y在不同监狱。
即通过+n,错过前n个数组位,在n+1~2*n的空间上建立一个扩展域来存放补集。

如果a,b罪犯在之前已经因为与c囚犯的怒气值分配进同一个监狱,就会发生如下情形:
A ( a , c ) > A ( a , b ) , A ( b , c ) > A ( a , b ) A_{(a,c)}>A_{(a,b)},A_{(b,c)}>A_{(a,b)} A(a,c)>A(a,b),A(b,c)>A(a,b);
则在遍历至 ( a , b ) (a,b) (a,b)对之前,就会有 f a t h e r [ a ] = c + n , f a t h e r [ b ] = c + n father[a]=c+n,father[b]=c+n father[a]=c+n,father[b]=c+n;
此时就会出现 f a t h e r [ a ] = f a t h e r [ b ] father[a]=father[b] father[a]=father[b];
即冲突无法避免,输出 A ( a , b ) A_{(a,b)} A(a,b)即可。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=100005;
int fa[N]={0};//n的数据范围是2e4,N是1e5,满足2*n
struct dat
{
    int a,b,c;
}da[N];
bool cmp(dat a,dat b)
{
    return a.c>b.c;
}
int find(int x)
{
    return fa[x]==x?x:fa[x]=find(fa[x]);
}
int main()
{
    int n,m,i;
    cin>>n>>m;
    for(i=1;i<=m;i++)scanf("%d%d%d",&da[i].a,&da[i].b,&da[i].c);//接收
    sort(da+1,da+1+m,cmp);//排序
    for(i=1;i<=2*n;i++)fa[i]=i;//初始化并查集
    for(i=1;i<=m;i++)
    {
        //printf("*%d %d\n",find(da[i].a),find(da[i].b));
        if(find(da[i].a)==find(da[i].b))//若无法分进不同监狱
        {
            printf("%d",da[i].c);
            return 0;
        }
        else//若可以
        {
            fa[find(da[i].a)]=find(da[i].b+n);//注
            fa[find(da[i].b)]=find(da[i].a+n);
        }
    }
    printf("0");//若所有都被分完,无冲突
    return 0;
}

注:这里若是fa[find(da[i].a)]=da[i].b+n;,则会爆MLE,迷惑

ED

\

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值