二分查找(2)
//poj 3273 Monthly Expense
/*
题意:给你天数n,和每天需要花的钱,让你把这些天分成m份(每份都是连续的天),
要求每份的和尽量少,输出这个和。
一开始二分的上界为n天花费的总和(相当于分成1份),下界为每天花费的最大值(相当于分成n份),
然后二分,每次的mid值为(上界 + 下界)/ 2,然后根据mid值遍历n天花费,对花费进行累加,
每当超过mid值 份数++,看看这个mid值能把n天分成几份,如果份数大于m,表示mid偏小,下界 = mid + 1,
反之小于等于mid,上界 = mid,然后输出最后的mid值即可,复杂度为 O(nlogM)
*/
#include <iostream>
using namespace std;
int list[100010];
int main()
{
int n,m,i,max=0,sum=0;
cin>>n>>m;
for(i=0;i<n;++i)
{
cin>>list[i];
sum+=list[i];
if(list[i]>max)
max=list[i];
}
int low=max,high=sum,mid,s,t;
while(low<high)
{
mid=(low+high)/2;
s=0;t=0;
for(i=0;i<n;++i)
{
s+=list[i];
if(s>mid)
{
t++;
s=list[i];
}
if(t>m)break;
}
if(s>0) //注意这句
t++;
if(t<=m)
//若t==m,此时不能断定m是最小的,可能有比它更小的数也符合,具有单调性,所以需要继续搜索前半部分
high=mid;
//这里不写成high=mid+1;因为可能除了mid符合外,并没有比它小的数也符合
else
low=mid+1;
//因为low<=mid<high,所以high=mid,low=mid+1,都会不断地缩小范围,最终low==high,跳出循环
}
cout<<low<<endl;
return 0;
}
//对应于 0011序列中最左侧的1的查找(最小值):
//http://duanple.blog.163.com/blog/static/709717672009049528185/