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
总之, 二分的边界问题非常麻烦,还是要具体问题具体分析。