poj3273 二分,以及关于二分的一些思考

题意:给定n个元素的一个序列,要求用隔板法将其分为m段,编程求出分成m段后,将每一段的数相加所得的最大值最小为多少

做法,枚举可能的数值,然后判断分段的段数,跟给定段数m进行比较,接着不断逼近即可。

#include <iostream>
#include <cstdio>
using namespace std;

int money[100005], n, k;

int BSearch(int l, int h, int k) {
    int mid, tmp, cnt;
    while (l < h) {
        mid = (l + h) >> 1;
        tmp = cnt = 0;
        for (int i = 1; i <= n; i++) {
            if (tmp + money[i] > mid) {
                cnt++;
                tmp = money[i];
            }
            else tmp += money[i];
        }
        cnt++;
        //================
        if (cnt <= k) h = mid;       
        else l = mid + 1;               
        //================
    }
    return h;
}

int main()
{
    int min, max;
    scanf ("%d%d", &n, &k);
    max = min = 0;
    for (int i = 1; i <= n; i++) {
        scanf ("%d", &money[i]);
        if (money[i] > min) min = money[i];
        max += money[i];
    }
    printf("%d\n", BSearch(min, max, k));

    return 0;
}
代码中被//==========夹住的部分之前我是这样写的

if (cnt >= k) l = mid;       
else h = mid - 1;   
提交结果TLE

然后思考一下,发现这个代码求的是上限,而非下限,如:假设501~601求出的段数都是题目要求的m段,那么我们所求解的答案应当是501,而非601

但是为什么这样写是TLE而不是wa?

再次思考后发现,是因为计算机整数除法的整除,如果不能整除就会向去尾去整,例如:当l = 601,h=602时;mid =(l+h)>> 1 = 601,

601是满足上面代码的if语句的,于是l=mid=601,这样,就会一直出现l=601,h=602,死循环,所以TLE。

反思:因为整除去尾这样的效应存在,为避免上面的情况,二分应该都是对 l 进行加1操作来逼近,而不能是对h进行减1操作来逼近。

还有就是if,else语句中也要注意等号放在不同地方,l,h该怎样操作(这点就需要结合题目要求求的是什么)

例如 这道题还可以这样写

if (cnt > k) l = mid + 1;
else h = mid;
但是必须是对 l 进行加1操作。

你可能感兴趣的:(poj3273 二分,以及关于二分的一些思考)