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