【NOIP2010】关押罪犯

本文探讨了一个罪犯分配问题,目标是最小化监狱冲突事件的最大影响力。通过两种解法——带权关系并查集和二分+二分图染色判断,文章提供了具体的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Problem

题目描述
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 <=1000000000,且每对罪犯组合只出现一次。

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

样例输入
4 6
1 4 2534
2 3 3512
1 2 28351
1 3 6618
2 4 1805
3 4 12884
样例输出
3512
提示
【输入输出样例说明】

罪犯之间的怨气值如下面左图所示,右图所示为罪犯的分配方法,市长看到的冲突事件

这里写图片描述
影响力是3512(由2 号和3 号罪犯引发)。其他任何分法都不会比这个分法更优。

【数据范围】
对于30%的数据有N≤15。
对于70%的数据有N≤2000,M≤50000。
对于100%的数据有N≤ 20000,M≤ 100000。

Solution

解法一:带权关系并查集
注意到只关注影响力最大的事件,我们可以按影响力降序排列,若有两位罪犯x,y,我们肯定要把他们分在两个监狱中关押,若是无法做到,那么就不用管后面的事件了,这个性质是题目的关键,也是解题的关键。我们用并查集来维护,val值0表示同一个监狱,1表示不同监狱,路径压缩很好推,知道不合条件即可。

解法二:二分+二分图染色判断
首先这道题目的答案呈现出单调性,显然答案越大越容易实现,换句话说,若x可行,可以排除x+1及更大,若x不可行,可以排除x-1及更小。
现在的问题是假如有一个答案mid,怎么确定是否有可行解?
显然我们只需要关心能否把影响力大于mid的事件对应的两个人分到不同监狱。我们把冲突大于mid的关系表示为连接两个点的一条边,问题就转化为了:
能否把所有的点分到两个集合中,并且所有的边连接的点分别属于两个集合,这就是二分图的定义!!!
判断一个图是否可以被二分:dfs/bfs染色即可

CODE

解法一:并查集

#include<bits/stdc++.h>
using namespace std;
const int MAXN=20010,MAXM=100010;
int fa[MAXN],val[MAXN];
int n,m,ans;

struct Edge{
    int x,y,z;
}e[MAXM];
bool cmp(Edge a,Edge b)
{
    return a.z>b.z;
}
int getfa(int x)
{
    if (x==fa[x]) return fa[x];
    int t=fa[x];
    fa[x]=getfa(fa[x]);
    val[x]=(val[x]+val[t])&1;
    return fa[x];
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        e[i]=(Edge){x,y,z};
    }
    sort(e+1,e+1+m,cmp);
    ans=0;
    for (int i=1;i<=n;i++) fa[i]=i,val[i]=0;
    for (int i=1;i<=m;i++)
    {
        int x=e[i].x,y=e[i].y;
        int fx=getfa(x),fy=getfa(y);
        if (fx!=fy)
        {
            fa[fy]=fx;
            val[fy]=(val[x]+1-val[y])&1;
        }
        else if (val[x]==val[y]) 
        {
            ans=e[i].z;
            break;
        }
    }
    printf("%d",ans);
    return 0;
}

解法二:二分+染色

#include<bits/stdc++.h>
using namespace std;
const int MAXN=20010,MAXM=100010;
int Head[MAXN],Next[MAXM*2],To[MAXM*2],Key[MAXM*2];
int color[MAXN],n,m,tot;
int l,r,mid;

void add(int x,int y,int z)
{
    tot++;
    Next[tot]=Head[x];
    Head[x]=tot;
    To[tot]=y;
    Key[tot]=z;
}
bool dfs(int u)
{
    for (int i=Head[u];i;i=Next[i])
    {   
        int v=To[i],w=Key[i];
        if (w<=mid) continue;
        if (color[v]==-1) 
        {
            color[v]=color[u]^1;
            if (!dfs(v)) return false;
        }
        else if (color[u]==color[v]) return false;      
    }
    return true;    
}
bool pd()
{
    memset(color,-1,sizeof(color));
    for (int i=1;i<=n;i++)
        if (color[i]==-1)
        {
            color[i]=0;
            if (!dfs(i)) return false;
        }   
    return true;
}
int main()
{
    scanf("%d%d",&n,&m);
    for (int i=1;i<=m;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        r=max(r,z);
        add(x,y,z); add(y,x,z);     
    }
    while (l<r)
    {
        mid=(l+r)>>1;
        if (pd()) r=mid;
        else l=mid+1;       
    }
    printf("%d",r);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值