https://vjudge.net/problem/OpenJ_Bailian-4135
题意: 给定n天花费, 要求将n天分为连续的m份, 最小化其和(最小连续和)
比较基础的二分算法问题, 拿到二分题的第一思路应该是寻找范围, 这道题里不难想出搜索的范围应该是 最大的月花费 <-> 所有的月花费之间. 最后求得的不是mid而是low
但直接去搜索费用的话, 又会导致费用的组成问题, 此时再来两个for循环, 复杂度又变回了n^2.
这时我们要改进一下传统的二分算法, 改进判断条件. 增加函数judge用以判断在满足开支最小值为mid时是否可行(也就是是否超过了M组).
因为题中要求日期必须相邻(开始忽略了这个问题, 这个条件使得问题大大简化), 所以顺序迭代即可
代码如下
//月度开销
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100010;
typedef long long LL;
LL low, high, mid;
int N, M, m[maxn];
int judge()
{ //判断在满足开支最小值为mid时是否可行
int sum = 0, cont = 1;
for(int i = 1; i <= N; i++){ //题意为连续的多天
sum += m[i];
if(sum >= mid){
cont++;
sum = (sum==mid ? 0 : m[i]);
if(i==N) --cont; //最后一组单独分组
}
}
if(cont > M) return false; //超出范围即错误
return true;
}
LL binarySearch()
{ //二分查找
while(high >= low){
mid = low+(high-low)/2;
if(judge()) high = mid-1; //分的数量较少, 缩小最大范围
else low = mid+1; //分的数量较多, 扩大最大范围
}
return mid;
}
int main()
{
scanf("%d%d",&N,&M);
low = high = 0;
for(int i = 1; i <= N; i++){
scanf("%d",&m[i]);
if(m[i] > low) low = m[i]; //left为最大月份开销和
high += m[i]; //right为所有月份开销和
}
printf("%lld\n",binarySearch());
return 0;
}