【题解】营救(二分答案)

营救

题目描述

一座摩天大楼起了大火,n个人都被困在了顶层狭长的走廊上,大家排着长长的队伍等着逃离险境。但火势很猛,消防员升起的救生舱只有m次运人下来的机会,并且每次运的人总重量还不能太重,避免将救生舱压垮。此时如何将这一排人分隔成m个连续的小组,(大家遵守逃生守则,没有人会往前插队),并且让这m个组中总重量最重的那个组的重量尽量小。这样才能快速安全的将大家都救离险境。
现在告诉你这n个人的体重,请你找出一种分组方法,让这m个组中总重量最重的那个组的重量尽量小,并输出这个组的总重量。

输入

第一行两个正整数n和m,中间用一个空格隔开,表示有n个逃生的人和要分隔成m个连续的小组。
第二行n个正整数,每个整数之间用一个空格隔开,表示n个人的体重(单位:公斤)。

输出

一个正整数,表示m个组中总重量最重的那个组的重量。

样例输入

6 3
20 30 50 80 100 120

样例输出

180

提示

一种合理的分法(20 30 50)(80 100)(120)
数据范围:
30% 1<=n<=10;

5<=m<=10;
70% 1<=n<=100;5<=m<=20;
100% 1<=n<=10000; 100<=m<=1000;


思路

二分答案的运用。求最重的那个组的重量,所以二分范围为n个人中的最大体重到总重,得到一个体重x再循环n遍,看在最多承载x重量的情况下分成几组,组数大于等于m就将x往大求,否则将x往小求。

ac代码:

#include <bits/stdc++.h>
using namespace std;
int n,m,c,res,a[10010],l,r,x;
int main()
{
    cin>>n>>m;
    for(int i=0; i<n; i++)
    {
        cin>>a[i];
        r+=a[i];       //右边界,总重
        l=max(l,a[i]); //左边界,最大体重
    }
    while(l<=r)
    {
        x=(l+r)/2;   //取得一个体重x
        res=0;
        c=0;
        for(int i=0; i<n; i++)
        {
            if(res+a[i]<x)
            {
                res+=a[i];//能塞就塞
            }
            else
            {
                res=a[i];
                c++;      //不能塞放下一组,组数加一
            }
        }
        if(c>=m) l=x+1; //大于等于规定组数m,x往大求
        else r=x-1;    //否则x往小求
    }
    cout<<(l+r)/2<<endl;//循环最后更新l,r,不能直接输出x
    return 0;
}

边界的思考

首先,对于题目样例

6 3
20 30 50 80 100 120

初始边界l=120, r=400。

这是每次循环的l, r, x, c

l=120   r=400   x=260   c=1
更新后:l=120   r=259
l=120   r=259   x=189   c=2
更新后:l=120   r=188
l=120   r=188   x=154   c=3
更新后:l=155   r=188
l=155   r=188   x=171   c=3
更新后:l=172   r=188
l=172   r=188   x=180   c=3
更新后:l=181   r=188
l=181   r=188   x=184   c=2
更新后:l=181   r=183
l=181   r=183   x=182   c=2
更新后:l=181   r=181
l=181   r=181   x=181   c=2
更新后:l=181   r=180

有一个问题,20行塞人的语句 res+a[i]<x 如果改成 res+a[i]<=x ,就是加上a[i]刚好等于额定的x,也把a[i]加到那一组,但是得到的结果是179,与答案不符,这次每次循环的l, r, x, c

l=120   r=400   x=260   c=1
更新后:l=120   r=259
l=120   r=259   x=189   c=2
更新后:l=120   r=188
l=120   r=188   x=154   c=3
更新后:l=155   r=188
l=155   r=188   x=171   c=3
更新后:l=172   r=188
l=172   r=188   x=180   c=2
更新后:l=172   r=179
l=172   r=179   x=175   c=3
更新后:l=176   r=179
l=176   r=179   x=177   c=3
更新后:l=178   r=179
l=178   r=179   x=178   c=3
更新后:l=179   r=179
l=179   r=179   x=179   c=3
更新后:l=180   r=179

 从红色段开始不同,研究之后我发现,代码中的c根本不是实际分的组数!而是实际组数-1 !!可是我把c每次初始设为1,塞人语句改为 res+a[i]<=x ,得到的结果是219???心态炸了。。。

凭借自己对二分的认识,我把ac代码中更新l的语句改为 l=x , 条件改while退出为 l<r ,样例能过,但是提交后就tle了。。。

研究了半天还是不知道为啥,看来我对二分的认识还非常非常浅显,这段代码a的莫名其妙的,如果有大佬看出要点的话,欢迎在评论区留言!

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值