YbtOj例题:二分1 数列分段

http://noip.ybtoj.com.cn/contest/14/problem/1

虽然这道题是例题,但是它非常的经典。所以我决定写一写。

通常,求解”最大值最小“或”最小值最大“一类的问题,都会用到二分算法。但仅凭这一个条件无法确定使用二分算法,下面举几个栗子

1.

(这是本题)

 总的分组数目有限制,二分有判定条件,可以二分。  答案转化为二分判定

2.

洛谷P4047部落划分

 部落总数有限制,可以二分。 答案转化为二分判定

3.

洛谷P4447 [AHOI2018初中组]分组

 虽然总的组数没有限制,但我们可以构造出一个单调上升的序列,p[x]表示第x组接下来需要接上的数的大小,它是单调递增的,我们可以二分 二分查找

4.

洛谷P1080国王游戏

这道题就不能二分,因为总钱数没有限制,我们也构造不出一个单调的帮助我们解题的序列。

回到这道题

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 5, inf = 0x3f3f3f3f;
int n, m, a[N];
int l, r = 0;
bool check(int x) {
    int s = 0, cnt = 1;
    for (int i = 1; i <= n; i++) {
        if (s + a[i] <= x)
            s += a[i];
        else {
            cnt++;
            s = a[i];
        }
    }

    return cnt <= m;
}
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
        l = max(l, a[i]);
        r += a[i];
    }
    while (l < r) {
        int mid = l + r >> 1;
        if (check(mid))
            r = mid;
        else
            l = mid + 1;
    }
    printf("%d", l);
    return 0;
}

 还有一个值得我们思考的地方:

bool check(int x) {
    int s = 0, cnt = 1;
    for (int i = 1; i <= n; i++) {
        if (s + a[i] <= x)
            s += a[i];
        else {
            cnt++;
            s = a[i];
        }
    }

    return cnt <= m;
}

是return cnt<=m,并用相应的二分形式,还是return cnt>=m并用相应的二分形式呢。

通过对最优解是奇数还是偶数的讨论,可以得到

若求最大值,就用小于等于,若求最小值,就用大于等于  maybe

 

总之, 二分的边界问题非常麻烦,还是要具体问题具体分析。
原文地址:https://www.cnblogs.com/smartljy/p/13433799.html