涉及知识点:
solution
- \(我们可以看到它让求的是最大值最小问题,诸如最大值最小,最小值最大这种问题,我们都可以使用二分答案来求解。\)
- \(即然要使用二分,我们就需要解决两个问题:\)
- \(1. 二分的左右边界是什么?\)
- \(左边界表示答案的最小取值,很显然,它最小的情况下只能是所给数的最大值,所以左边界取数列的最大值\)
- \(右边界表示答案的最大取值,由于本题说明给出的数都为正整数,所以只要把所有数相加就是右边界\)
- \(2. 二分的判断条件是什么?\)
- \(我们可以假设当前的mid为我们的答案,然后按照条件判断。\)
- \(从头按照将mid看成最大值来分区域,如果划分的区域数小于等于规定的区域数则为真,否则为假。\)
代码
#include <iostream>
#include <cstdio>
using namespace std;
const int N = 1e5+10;
int n,m;
int a[N];
int l=-1,r=0;
bool check(int x)
{
int i = 0;//表示当前遍历到第几个数
int section = 1;//以x为最大值划分的区域个数,初始为1
int sum = 0;//表示当前区域的和
while (i <= n-1)//由于循环里无论结果怎样i都会加一,所以只要循环到n-1就可以完成1~n的循环
{
if(sum + a[i+1] <= x) sum += a[++i];//如果下一个数加上sum还没到x,则继续遍历
else sum = a[++i],section++;//如果下一个数加上sum比x要大,则下一个数就是一个新的区域
}
if(section <= m) return true;//如果划分的区域数比m小,返回true
else return false;//如果划分的区域数比m大,返回false
}
int main()
{
scanf("%d%d",&n,&m);
for (int i = 1 ; i <= n ; i ++ )
{
scanf("%d",&a[i]);
l = max(l,a[i]);//左边界取数列的最大值
r = r + a[i];//右边界为数列的和
}
while (l < r)
{
int mid = l + r >> 1;
if(check(mid)) r = mid;//如果划分的数量比m小,说明mid过大,则答案在mid左边或等于mid
else l = mid+1;//如果划分的数量比m大,说明mid过小,则答案在mid右边
}
printf("%d\n",l);
return 0;
}