https://vjudge.net/problem/OpenJ_Bailian-4135

Monthly Expense OpenJ_Bailian - 4135 (二分)

比较基础的二分算法问题, 拿到二分题的第一思路应该是寻找范围, 这道题里不难想出搜索的范围应该是 最大的月花费 <-> 所有的月花费之间. 最后求得的不是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;
}

 

相关文章: