链接:P2985
题目描述
Bessie拿到了N (1 <= N <= 50,000)块巧克力。她决定想个办法吃掉这些巧克力,使得它在吃巧克力的这段时间里,最不开心的一天尽可能的开心。并且一共吃D (1 <= D <= 50,000)天。
每块巧克力有一个开心值H_i (1 <= H_i <= 1,000,000),当某天你吃下那块巧克力时,你将获得那块巧克力的开心值。每一天的开心值是所有当天吃掉的巧克力的总开心值之和。每天晚上Bessie睡觉之后,它的开心值会减半。也就是说,比如昨天Bessie的开心值为50,那么今天早上我一醒来我就会有25点的开心值,舍去小数点后数字。另外,Bessie还有一个怪癖,她喜欢按照巧克力本来的排列顺序吃。
Bessie第一天的开心值为0,求一个每天吃巧克力的方案,使得Bessie最不开心的一天尽可能的开心。
输入格式
-
Line 1: Two space separated integers: N and D
-
Lines 2…N+1: Line i+1 contains a single integer: H_i
输出格式
-
Line 1: A single integer, the highest Bessie’s minimum happiness can be over the next D days
-
Lines 2…N+1: Line i+1 contains an integer that is the day on which Bessie eats chocolate i
输入输出样例
输入 #1
5 5
10
40
13
22
7
输出 #1
24
1
1
3
4
5
(输入第2到N+1行是每块巧克力的快乐值
输出第一行是每天得到的快乐值,2到N+1行是第i块巧克力在第几天被吃)
一看到题目显然易见得是二分答案,但是我一直都调不过去。
还好今天早上有大佬在机房讲解了这道题才让我略懂一二,把这道题过了。
我们二分每天得到的快乐值的大小,判断这个快乐值如果把巧克力都吃了都得不到则缩小范围,否则就尽可能将这个快乐值放大。
在每一次二分判断是否符合条件的时候,如果符合条件就可以先把此时每块巧克力吃的天数给用数组存储起来了。
但是明明是黄题的难度为什么会被升到蓝题呢?
因为其中有很多的坑点,如果简单地把这道题按照这个思路打出来不考虑一些特殊情况和细节方面,很难可以ac(没错我又看了题解才A的
看一下代码再慢慢说坑点:
代码:
#include <cstdio>
#include <iostream>
#define LL long long
using namespace std;
LL n,m,d[100001],a[100001],ans;
int check(LL x)
{
LL sum=0,k=0;
for (LL i=1;i<=m;i++) //枚举天数
{
sum/=2; //第一个坑点: 每天一起床快乐值减半
while (sum<x) //一直吃巧克力直到吃的巧克力所得到的快乐值大于当前二分得到的每天快乐值
{
k++;
sum+=a[k];
if (k>n) //如果当前设定的这个每天的快乐值太大,无法达到就要缩小范围
return 0;
if(x&&x==ans) d[k]=i; //在二分寻找的答案的过程中随时更新巧克力吃的时间
}
}
return 1;
}
int main()
{
LL L=0,R=0,mid;
scanf("%lld%lld",&n,&m);
for (LL i=1;i<=n;i++)
scanf("%lld",&a[i]),R+=a[i]; //R的最大值,即二分的右边界
while (L<=R)
{
mid=(L+R)/2;
if (check(mid))
ans=mid,L=mid+1;
else
R=mid-1;
}
printf("%lld\n",ans);
check(ans); //第二个坑点:找到最优答案后要再扫一遍更新一下d数组,因为如果不扫一遍的话day[]的值可能还会停留在上一阶段。
for (LL i=1;i<=n;i++)
{
if (d[i]>0)
printf("%lld\n",d[i]);
else
printf("%lld\n",m); //第三个坑点:题目要求的蜜汁字典序,所以没吃掉的要在最后一天全吃完
}
return 0;
}