算法入门-二分答案-整点巧克力?
整点巧克力
https://ac.nowcoder.com/acm/contest/22353/1003
题意
- 给定n块巧克力d天,第i块巧克力提供热量ai,每天热量会减半
- 请给出最大的最小热量,使得在d天中每天的热量值都大于等于最小热量
思路
- 观察可得,最小热量越大维持天数越小,满足单调性,二分最小热量,对每一个最小热量判断是否足够维持d天
- 注意各种细节
细节(狂WA9发,人已经麻啦)
- 虽然每一块巧克力都是2e5以内的范围,但加和可能超过int——不开long long见祖宗
- 左右界:在本题中左界似乎不影响,但是右界得设的足够大如1e16,所以建议以后考虑二分的时候把右界算出来,在这个题中右界为所有热量值的加和
- 特殊情况思考,维护最后的吃巧克力顺序时,有可能有的巧克力吃了也不会增加天数,所以在judge中会以没吃的形式存在,所以输出顺序的时候,要把这些补到最后一天吃
- 本题其实不用在judge中判断每一种情况吃完后能维护多少天然后再和d比较,这样会增加很多细节,比如天数超过d天,顺序无法直接放,以及吃完后还能满足若干天,然后把这若干天补充到判断中
- 更为高效的方法其实是直接模拟吃d天,只要有一天不够吃,直接return 0 ,如果能维持d天多出来的就都放到最后一天吃
随感
- 今天是不适合敲代码的一天,昨天回本部打了场选拔赛,好几个题开了都没写AC,幸亏是IOI赛制,还能捞点部分分,仔细想来昨天的二分似乎也是细节不太合理,以及广搜的思路不对,说白了还是菜,还得多练TT
AC代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int h[2020202];
int t[2020202];
int n,d;
bool judge(int p){
long long sum=0;
int cnt=1;
for(int i=0;i<d;i++){
while(sum<p&&cnt<=n){
sum+=h[cnt++];
}
if(sum<p)return 0;
sum/=2;
}
return 1;
}
void eat(int e){
long long sum=0;
int cnt=1;
for(int i=0;i<d;i++){
while(sum<e&&cnt<=n){
sum+=h[cnt++];
printf("%lld\n",i+1);
}
sum/=2;
}
while(cnt<=n){
printf("%lld\n",d);
cnt++;
}
}
signed main(){
int l=1,r=0;
scanf("%lld%lld",&n,&d);
for(int i=1;i<=n;i++){
scanf("%lld",&h[i]);
r+=h[i];
}
while(l<=r){
int mid=(l+r)>>1;
if(judge(mid)){
l=mid+1;
}else{
r=mid-1;
}
}
printf("%lld\n",l-1);
eat(l-1);
return 0;
}