天天看点

P1182 数列分段 Section II 题解

题目传送门

一、为什么可以使用二分,怎么想到的?

每段和的最大值最小。

,提示的很明显,什么最大值最小,最小值最大等等,均可以视为二分答案题。为啥呢?因为二分的本质是区间逼近,这样才能求出极值,不用二分你说用啥?

二、二分查找的是什么?

本题查找的肯定是

每段和的最大值

,逼近的是让它最小。

三、左右边界值是多少?

每段和的极限情况:(1)一个数字一段;(2)所有数字在一段里。

每个数字独自一段的话,最大值就是\(max(a[i])\)

所有数字在一段的话,最大值就是\(sum(a[i])\)

四、是向左逼近,还是向右逼近?

求最大值的最小。即向左逼近

五、check函数的实现

按东北话来讲,就是:逮着一个往死了加,直到冒了就再开一个房间~

最终是否符合是题意要求就是: \(cnt<=m\)

六、C++代码

#include <bits/stdc++.h>

using namespace std;
const int N = 100010;
int a[N];
int n, m;

//检查函数
bool check(int mid) {
    int cnt = 1; //最小一个区间
    int sum = 0; //区间和
    for (int i = 1; i <= n; i++) {
        //区间和计算
        sum += a[i];
        //如果超过了mid值
        if (sum > mid) {
            cnt++; //需要下一个段了~
            sum = a[i];
        }
    }
    //是否符合题意的要求,在m个区间段范围内?
    return cnt <= m;
}

int l, r;

int main() {
    cin >> n >> m;
    //输入
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        l = max(l, a[i]);
        r += a[i];
    }

    while (l < r) {
        int mid = l + r >> 1;//mid的定义:每段和
        if (check(mid)) r = mid;//向左逼近
        else l = mid + 1;
    }
    //输出大吉
    cout << l << endl;
    return 0;
}