题目:
贝西从公牛那里收到了 N (1 <= N <= 50,000) 巧克力,但不想吃得太快,所以她想计划下一个 D (1 <= D <= 50,000) 天的巧克力吃时间表,以便在这些日子里最大限度地提高她的最低幸福水平。 Bessie 的幸福感是一个整数,从 0 开始,并在她睡觉时在一夜之间减半(必要时四舍五入)。然而,当她吃巧克力 i 时,她的幸福感会增加整数 𝐻 我 H 我 (1 <= 𝐻 我 H 我 <= 1,000,000)。如果她在某一天吃巧克力,那么她当天的幸福感就被认为是她吃完巧克力后的幸福程度。贝西坚持要按照收到的顺序吃巧克力d 他们。 如果存在多个最优解,请打印其中任何一个。 考虑在 5 天内吃掉 5 块巧克力;它们分别带来幸福(10、40、13、22、7)。 如果贝西在第一天吃了第一块巧克力(10 幸福),然后等着吃其他巧克力,那么第一天之后她的幸福度是 10。
前言与题解35一样,依旧是一道最值问题,并且固定量比较多(天数,吃巧克力的个数及顺序),考虑二分枚举,然后去验证
Line 1: Two space separated integers: N and D
第 2到N+1 行:第 i+1 行包含一个整数,该整数是 Bessie 吃巧克力 i 的那一天 的心情增加值
思路&AC代码:
#include<iostream>
#include<cstring>//memset()
#define int long long//二分枚举中,如果第一天就把所有巧克力吃完了,可达5e10,超过了2e9
using namespace std;
int chocolate[50010];//各个巧克力能加的心情
int n,d,cnt;//巧克力个数,天数,当前吃的个数
int eat[50010];//存储第几天吃了多少块
bool solve(int happy)
{
int curhappy=0;
cnt=1;
int i;
for(i=1;i<=d;++i)
{
while(curhappy<happy)//每一天的初始心情值(前一天吃完/2)不够假设的最大的最低心情值就吃巧克力(有顺序要求)
{
if(cnt>n) {return false;}//一上来就判断,防止多操作
curhappy+=chocolate[cnt];
++eat[i];
++cnt;
}
curhappy>>=1;// /=2
}
return true;//中间的cnt>n已经判断过了,这里必然cnt<=n && curhappy>=happy
}
signed main()//写完了才想起把int改long long,就要这么写main
{
ios::sync_with_stdio(0);cin.tie(0);
int sum=0;//记录下能够达到的最大心情值(一天全吃完)
cin>>n>>d;
for(int i=1;i<=n;++i)
{
cin>>chocolate[i];
sum+=chocolate[i];
}
int l=0,r=sum,mid=(l+r+1)>>1;//我习惯这么写,这样写只要在找最大值时写成(l+r+1)就行
while(l<r)
{
if(solve(mid)) {l=mid;}
else{r=mid-1;}
mid=(l+r+1)>>1;
}
memset(eat,0,sizeof(eat));//清空eat,留给后面重新计算
solve(l);//这里重新算有两个原因.一是eat已经乱了(也可以增加一个标记值判断是不是最后一次计算,决定每次记不记录eat。二是若最后一次l+1==r时,mid=r,如果solve(r)不服题意,则r=mid-1 ==l,最后一次记的eat是l+1(r)的,而solve(r)不符题意,答案不正确,需要调用正确的solve(l)
cout<<l<<'\n';
for(int j=1;j<=d;++j)
{
while(eat[j]!=0) {cout<<j<<'\n';--eat[j];}//eat[j]是几就输出几次
}
for(int i=cnt;i<=n;++i) {cout<<d<<'\n';}//最后满足要求的一次多了一个++cnt。题目要求巧克力全吃完,所以剩下的全放最后一天吃就好。因为此时答案已经不能再增加了(二分枚举得出的结果),所以剩下的巧克力什么时候吃都行
return 0;
}