题意:
把一个包含n个正整数的序列划分成m个连续的子序列。设第i个序列的各数之和为S(i),求所有S(i)的最大值最小是多少?
显然答案的范围是【整个数组的最小值,整个数组之和】
二分这个范围,对于每次二分得到的X,判断是否合法
判断函数就是从左到右 对每个块尽量选够X,最后看 得到的块数,如果超过m,必定 小于等于X的所有数都不合法,那么表示答案在 【L,mid】的范围内内
如果得到的块数小于等于M,那么表示答案在 【L,mid】的范围内内
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
#include <queue>
#include <map>
#include <set>
#include <vector>
using namespace std;
const __int64 inf=10000000;
__int64 n,m;
__int64 tm[100005];
__int64 ok(__int64 x)
{
__int64 cnt= 0; //表示分成了多少块
__int64 i;
for (i=1;i<=n;i++)
{
if (tm[i]>x) return 0;
__int64 sum=0;
__int64 j=i;
for (;j<=n;j++)
{
if (tm[j]+sum>x) {j--;break;}
sum+=tm[j];
}
cnt++;
i=j;
}
return cnt<=m;
}
int main()
{
while(scanf("%I64d%I64d",&n,&m)!=EOF)
{
__int64 minn=inf;
__int64 sum=0;
__int64 i;
for (i=1;i<=n;i++)
{
scanf("%I64d",&tm[i]);
if (tm[i]<minn)
minn=tm[i];
sum+=tm[i];
}
__int64 l=minn;
__int64 r=sum;
while(l<=r)
{
__int64 mid=(l+r)/2;
if (r-l==0) //只有一个待选答案
break;
if (r-l==1)//2选1
{
if (!ok(l)) l=r; //如果l不行答案就是r
break;
}
if (ok(mid))//不断逼近,确保每次[L,R]内都是可能的合法答案
r=mid; //mid可能合法
else
l=mid+1; //mid一定不合法
}
printf("%I64d\n",l);
}
return 0;
}