题意:给定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操作。