Contest3145 - 2021级新生个人训练赛第37场_J: 吃萝卜

//
问题 J: 吃萝卜
时间限制: 1.000 Sec  内存限制: 128 MB
 
题目描述
在一个神奇的国度里,有一只编程兔,它每天都写很多的代码,各种编程语言如pascal、c、c++、java、basic等等它都了如指掌,各种算法也都已经滚瓜乱熟了。小花是它的好朋友,经常和它一起玩耍。
某一天,小花给编程兔送来了很多的萝卜。编程兔很开心,决定把它的萝卜和其它的小兔子一起分享。小花共计送来了n袋萝卜(编号1到n),每袋里面都有一定数量的萝卜。小兔子共计有m只,兔子们都很守规矩,按照编号1到m依次排好领取萝卜,萝卜按照编号从小到大的顺序依次发放(也就是编号小的兔子领取前面的萝卜,编号大的兔子领取后面的萝卜,萝卜一定要分完,不能有剩余),每只兔子都只能领取连续的若干袋萝卜,每只兔子至少领取一袋萝卜,一袋萝卜也只能分给一只兔子,不能分给两只以上的兔子。
编程兔希望萝卜尽量能分的平均一点(否则小兔子们要不开心的^_^),也就是它希望得到萝卜最多数量的兔子的萝卜要最少。这个问题对于编程兔来说很简单,亲爱的同学们,你们会么?

输入
第一行是两个正整数n和m,表示萝卜的袋数和兔子的数量。
第二行是n个正整数,表示每袋萝卜的数量。
输出
输出只有一行一个整数,表示得到萝卜最多的那只兔子最少可以得到的萝卜数量。

样例输入 Copy
9 3
1 2 3 4 5 6 7 8 9
样例输出 Copy
17
提示
第1-5袋萝卜分给第一只兔子,总数是15个萝卜,第6-7袋萝卜分给第二只兔子,总数是13个萝卜,第8-9袋萝卜分给第三只兔子,总数是17个萝卜,萝卜最多的兔子得了17个萝卜,这是最多的兔子得到的最少的情况。如果第1-4袋分给第一只兔子,共计10个萝卜,第5-7袋分给第二只兔子共计18个萝卜,第8-9袋分给第三只兔子,共计17个萝卜,这样最多的兔子得到了18个萝卜,比之前的方案大,所以不是最优。

对于60%的数据,1<=m<=n<=100,每袋萝卜的数量不超过10。
对于100%的数据,1<=m<=n<=100000,每袋萝卜的数量不超过10000。

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

// 1<=m<=n<=100000,每袋萝卜的数量不超过10000 1e5*1e4=1e9 < int
const int MAXN=1e5+6;

int a[MAXN];
int n,m;

bool half( int mid )
{
    int i,sum,cnt;
    sum=cnt=0;

    for( i=0;i<n;i++ )
    {
        if( sum+a[i]<=mid ) sum+=a[i];
        else        { cnt++; sum=a[i]; }
    }
    cnt++;                          // 未超 mid || 恰剩一袋 

    if( cnt>m ) return false;       // 分堆多 兔子数量不够
    else        return true;        // 分堆少 可拆堆
}

int main()
{
    int i,x,y,mid;

    while( ~scanf("%d%d",&n,&m) )
    {
        x=y=0;
        memset( a,0,sizeof( a ) );
    
        for( i=0;i<n;i++ )
        {
            scanf("%d",&a[i]);
            x=max( x,a[i] );
            y+=a[i];
        }

        while( x<y )
        {
            mid=( x+y )>>1;
            if( half( mid ) )   y=mid;      // y=mid 也可以成为终结点
            else                x=mid+1;
        }
        printf("%d\n",x);
    }
    return 0;
}

//

Q:怎么保证一定存在 某一堆 == mid?
A:
    合理定义初始边界 + half 条件 + while

    01 由题意 合理定义初始边界 减少查找次数 ( 阅读理解能力 )

    02 half 条件

        01 如果 mid 小了 堆分多了 兔不够 下界x == mid+1
        02 如果 mid 大了 堆分少了 要拆堆 上界y == mid

        最终定位到的就是 恰好存在 某一堆 == mid 

    03 while

    查找过程中 确实存在 cnt<m 的情况出现 但 y=mid 将它消灭 最终结果只能是 cnt==m

    当 cnt==m 时 x!=y, while 会继续查找.

    只有当 cnt==m && 恰好存在 某一堆 == mid 时 while 才会结束.

    eg.
        0 0 1 1 2 1 1 0 0

    0 已排除的 mid
    1 cnt==m 的 mid
    2 ans

    只有当 x==y 时 while 循环才会停止 才会输出结果

//

eg.
    5 2
    3 2 4 10 7 (26)
    10 26
    10 18
    14 18
    16 18
    16 17
    17 17   // 左右逼近

    // y=mid; 终结
    5 2
    100 1 2 3 4 (110)
    100 110
    100 105
    100 102
    100 101
    100 100 // 向左逼近 ( 初始x 就是ans )

    // x=mid+1; 终结
    4 2
    100 1 100 1 (202)
    100 202
    100 151
    100 125
    100 112
    100 106
    100 103
    100 101
    101 101 // 当 x 不成立 y 成立时 y 就是结果

    2 2
    100 1 (101)
    100 101
    100 100 // 极端情况

    3 2
    100 150 1 (251)
    100 251 
    ... ...
    151     // 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

这题AC再睡.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值