总时间限制:1000ms
内存限制:65536kB
描述:农夫约翰是一个精明的会计师。他意识到自己可能没有足够的钱来维持农场的运转了。他计算出并记录下了接下来 N (1 ≤ N ≤ 100,000) 天里每天需要的开销。
约翰打算为连续的M (1 ≤ M ≤ N) 个财政周期创建预算案,他把一个财政周期命名为fajo月。每个fajo月包含一天或连续的多天,每天被恰好包含在一个fajo月里。
约翰的目标是合理安排每个fajo月包含的天数,使得开销最多的fajo月的开销尽可能少。
输入:第一行包含两个整数N,M,用单个空格隔开。
接下来N行,每行包含一个1到10000之间的整数,按顺序给出接下来N天里每天的开销。
输出:一个整数,即最大月度开销的最小值。
样例输入
7 5
100
400
300
100
500
101
400
样例输出
500
提示
若约翰将前两天作为一个月,第三、四两天作为一个月,最后三天每天作为一个月,则最大月度开销为500。其他任何分配方案都会比这个值更大。
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
bool isok(int ans);
int n,m,a[100005];
int main(){
int i,sum=0,max=0;
scanf("%d%d",&n,&m);
for(i=0;i<n;i++){
scanf("%d",&a[i]);
if(a[i]>max)
max=a[i];
sum+=a[i];
}
int left=max;
int right=sum;
int ans;
while(left<=right){
ans=left+(right-left)/2;
if(isok(ans))//如果可以这样分,就尝试把月度开销弄大点
left=ans+1;
else
right=ans-1;//如果不可以,就说明月度开销太大了,因该弄小点
}
printf("%d\n",ans);
system("pause");
return 0;
}
bool isok(int ans){
int sum=0,count=0,i;
for(i=0;i<n;i++){
if(sum+a[i]>ans){
count++;
sum=a[i];
}
else
sum+=a[i];
}
if(count>=m)return true;
return false;
}
思路:二分法,初始左端点是所有月份的最大值,右端点是所有月份花销总额。在isok函数里,如果前几个月的花销总额(sum)加上当前月大于所尝试的ans,那么就把这个月放到下一个fajo月里,然后isok里的sum改成当前月的花销,重新加,同时count++,最后看看count是否大于m。
这个count指的是最少能分离出几个fajo月,如果最少分离出来的fajo月数都小于m,就说明这样分不可以。