关押罪犯-

欢迎大家访问博客:博客传送门

关押罪犯


题目描述

image-20210714161314811


核心思路

从题干中,警察局长会把怒气值从大到小排序送给市长看,而市长只会看第一个最大的那个怒气值,如果这个最大的怒气值达到某种程度,则警察局长会被撤职。题目想让我们求的就是:有没有一个分配囚犯的方案,使得那个最大的怒气值,能够达到最小。

一般求 最大值最小 或者 最小值最大,都可以考虑用二分算法。 这里是想求最大值最小,因此,我们可以考虑一下使用二分算法。那么该如何进行二分呢?

考虑这样一个判定问题:是否存在一种分配罪犯的方案,使得 Z Z Z市长看到的那个冲突事件的影响力不超过 m i d mid mid。对于当 m i d mid mid满足要求时,那么所有大于 m i d mid mid也是满足要求的。也就是说题目的答案是具有单调性的,一旦具有单调性,那么一定可以用二分算法。

问题:为什么对于当 m i d mid mid满足要求时,那么所有大于 m i d mid mid也是满足要求的呢?

设当前二分的值为 m i d mid mid,那么对于那些怒气值大于 m i d mid mid的囚犯应该关在不同的监狱。假设它俩关在同一所监狱,那么他们会产生更大的影响力(设其值为 a a a),这个影响力会大于现优解 m i d mid mid,即 a > m i d a>mid a>mid,那么 a a a就要在排行榜上当第一了,又由于市长只看第一,因此市长看到更大的 a a a就会撤换警察局长,相对来说如果让 m i d mid mid在排行榜上当第一,那么影响力会更小一些。

举个栗子,假设当前 m i d = 50 mid=50 mid=50,如果有两个囚犯,他们之间的怒气值为 100 100 100,假设它俩关在同一个监狱 A A A,那么监狱 A A A就会产生影响力 100 100 100,但是如果我们让它俩关在不同的监狱 A A A B B B,那么此时监狱 A A A产生的影响力为0。因此,我们发现,当怒气值大于 m i d mid mid时,我们就更应该把它俩放到不同的监狱,这样会使得同一个监狱内的怒气值相对降低。但是如果他们之间的怒气值小于 m i d mid mid,那么就算把他俩放到不同监狱,此时排行榜上第一仍然是 m i d mid mid,并没有让第一的值减小,反而还浪费了一次分配机,这导致后面如果有大于 m i d mid mid的囚犯本应该被分配到两个不同的监狱却因为位置不够而被迫关在同一个监狱,导致这个监狱内的怒气值大于 m i d mid mid,排行榜上第一的值增大,这与我们想要的结果事与愿违。

这也就证明了 当 m i d mid mid满足要求时,那些怒气值大于 m i d mid mid就更能满足要求了,而那些怒气值 ≤ m i d \leq mid mid的就不满足条件了。

二分答案,设当前二分的值为 m i d mid mid。此时,任意两个仇恨程度大于 m i d mid mid的囚犯都必须被安排在不同的监狱。我们把每个囚犯都看作是一个节点,如果两个囚犯之间的仇恨程度大于 m i d mid mid,那么我们就在这两个囚犯之间连一条无向边,这样最终就会得到一张无向图。由于两个囚犯被分配到两个不同的监狱,因此这两个节点属于不同的点集合,也就是说这张无向图需要被分成两个点集合。由于我们把怒气值大于 m i d mid mid的两个囚犯分别关到两个不同的监狱中了,那么此时同一个监狱内的囚犯之间的怒气值必然是 ≤ m i d \leq mid mid。而我们定义了当怒气值大于 m i d mid mid才连边,所以对于怒气值 ≤ m i d \leq mid mid都不需要连边。也就是说每个集合内部都没有边(同一个监狱内没有仇恨程度大于 m i d mid mid的罪犯)。我们发现,这种把节点划分成两种点集合,并且只能从一个点集合向另一个点集合连边,这种做法不正是二分图的定义嘛?

刚开始,我们对输入的数据都进行无向图 T T T,然后再这张图中,如果边权怒气值 ≤ m i d \leq mid mid,那么我们就不看这些边(可以认为是从原图 G G G中删除这些边),然后如果边权怒气值 > m i d >mid >mid,则保留,那么最终全部保留下来的这个子图也就是一张无向图,设为 G G G。然后我们对这个无向图 G G G判定是否为二分图。

因此总的算法思路如下:

  • 先二分一个答案 m i d mid mid,此时 m i d mid mid其实就是对应一种分配罪犯的方案。
  • 对每一个$ mid , 都 使 用 染 色 法 来 c h e c k 一 下 , 来 判 定 这 个 无 向 图 ,都使用染色法来check一下,来判定这个无向图 使checkG$是否为二分图
    • 如果 c h e c k ( m i d ) check(mid) check(mid)成功,则说明是二分图,点 m i d mid mid位于右侧绿色满足性质的区间,那么对于所有大于 m i d mid mid的分配方案都是合理的,即 m i d mid mid右侧都满足,为了求最小,我们让 r = m i d r=mid r=mid,往左侧收缩,找到最小值
    • 如果 c h e c k ( m i d ) check(mid) check(mid)失败,则说明不是二分图,点 m i d mid mid位于左侧红色不满足性质的区间,那么此时应该让左侧边界跳到 m i d mid mid的下一个位置,即 l = m i d + 1 l=mid+1 l=mid+1,这样才能满足性质

那么该如何确定二分的边界呢?由于题目说了 0 ≤ c ≤ 1 e 9 0\leq c\leq 1e9 0c1e9,因此答案最小值为 0 0 0,最大值为 1 e 9 1e9 1e9,因此二分的区间为[0,1e9]

题目要求“如果本年内监狱中未发生任何冲突事件,请输出 0 0 0”,这个我们并不需要特殊处理,因为我们二分的答案区间中就包含了 0 0 0。因此我们的算法一定可以考虑到这种情况。

设二分的性质为 当同一个监狱的两个囚犯之间的怒气值很大,则分配到两个不同的监狱中

image-20210714170229547


代码

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
//题目给的边数为1e5 但是由于是二分图 它是无向图 所以边数要开2倍
const int N=2e4+10,M=2e5+10;
int n,m;
int h[N],e[M],ne[M],w[M],idx;
int color[N];
void add(int a,int b,int c)
{
    e[idx]=b;
    w[idx]=c;
    ne[idx]=h[a];
    h[a]=idx++;
}
//染色法判定二分图
bool dfs(int u,int c,int mid)
{
    color[u]=c;
    for(int i=h[u];~i;i=ne[i])
    {
        int j=e[i];
        //在mid左侧的都在红色不满足性质的区间
        if(w[i]<=mid)
        continue;
        //如果j还没有被染色
        if(!color[j])
        {
            if(!dfs(j,3-c,mid))//染色有矛盾
            return false;
        }
        //j被染过色了,但是它的颜色与相邻节点的颜色都是相同的 染色有矛盾
        else if(color[j]==c)
        return false;
    }
    //说明染色无矛盾  是二分图
    return true;
}
bool check(int mid)
{
    memset(color,0,sizeof color);
    for(int i=1;i<=n;i++)
    {
        if(!color[i])
        {
            if(!dfs(i,1,mid))
            return false;
        }
    }
    return true;
}
int main()
{
    memset(h,-1,sizeof h);
    scanf("%d%d",&n,&m);
    while(m--)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        //建图
        add(a,b,c);
        add(b,a,c);
    }
    int l=0,r=1e9;
    //二分答案
    while(l<r)
    {
        int mid=l+r>>1;
        //判断mid是否在绿色满足性质的区间 如果在则说明是二分图
        if(check(mid))
        r=mid;
        else
        l=mid+1;
    }
    printf("%d\n",l);
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卷心菜不卷Iris

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值