不光是查找值!“二分搜索”
最大化最小值
POJ 3258 N块石子,要移去M块,求剩余石子之间距离的最小值的最大值
二分答案

1 #include <algorithm>
2 #include <cstdio>
3 #include <iostream>
4 using namespace std;
5
6 int a[50005];
7 int l, m, n;
8
9 bool C(int d) {
10 int last = 0, t = 0;
11 for (; last <= n;) {
12 int crt = last + 1;
13 while (crt <= n + 1 && a[crt] - a[last] < d) crt++, t++;
14 if (crt > n + 1 || t > m) return false;
15 last = crt;
16 }
17 return true;
18 }
19
20 int main() {
21 scanf("%lld%d%d", &l, &n, &m);
22 for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
23 sort(a + 1, a + n + 1);
24 a[n + 1] = l;
25 int lb = 0, ub = l + 1;
26 while (ub - lb > 1) {
27 int mid = (ub + lb) / 2;
28 if (C(mid))
29 lb = mid;
30 else
31 ub = mid;
32 }
33 printf("%d", lb);
34 }
POJ 3273 给出N天的预算,要划分成M组,求所有组中的最大值的最小值
二分答案

1 #include <algorithm>
2 #include <cstdio>
3 #include <iostream>
4 using namespace std;
5
6 int a[100005];
7 int n, m;
8
9 bool C(int sum) {
10 int last = 0;
11 for (int i = 0; i < m; i++) {
12 int crt = last + 1, r = a[last];
13 while (crt < n && r + a[crt] <= sum) {
14 r += a[crt];
15 crt++;
16 }
17 if (crt == n) return true;
18 last = crt;
19 }
20 return false;
21 }
22
23 int main() {
24 scanf("%d%d", &n, &m);
25 for (int i = 0; i < n; i++) scanf("%d", &a[i]);
26 int lb = *max_element(a, a + n) - 1, ub = (int)1e9;
27 while (ub - lb > 1) {
28 int mid = (lb + ub) / 2;
29 if (C(mid))
30 ub = mid;
31 else
32 lb = mid;
33 }
34 cout << ub;
35 }
POJ 3104 有一堆湿衣服,每秒蒸发1水分,还有一个蒸干器,每秒令一件衣服蒸发k水分,求所有衣服都干的最少时间
二分答案。注意用蒸发器时其实是加速蒸发k-1而不是k,要特判k=1避免除0

1 #include <cstdio>
2 #include <cstdlib>
3 using namespace std;
4 #define ll long long
5
6 const int maxn = 1e5 + 5;
7
8 int a[maxn], n, k;
9
10 int max(int a, int b) { return a > b ? a : b; }
11
12 bool C(int x) {
13 ll res = 0;
14 for (int i = 0; i < n; i++) res += (max(0, a[i] - x) + k - 2) / (k - 1);
15 return res <= x;
16 }
17
18 int main() {
19 scanf("%d", &n);
20 for (int i = 0; i < n; i++) scanf("%d", &a[i]);
21 scanf("%d", &k);
22 if (k == 1) {
23 int res = 0;
24 for (int i = 0; i < n; i++) res = max(res, a[i]);
25 printf("%d\n", res);
26 exit(0);
27 }
28 int l = 0, r = (int)1e9 + 1;
29 while (r - l > 1) {
30 int mid = (l + r) / 2;
31 if (C(mid))
32 r = mid;
33 else
34 l = mid;
35 }
36 printf("%d\n", r);
37 }
POJ 3045 有N头奶牛,具有体重w和力量s,它们叠在一起,每一只奶牛的risk值为它上方奶牛w之和减去自身s;求所有risk的最大值的最小值
考虑risk = sum_w(上方)- s(自身)= sum_w(上方+自身) - w(自身)- s(自身)。sum_w(上方+自身) 在未确定剩余奶牛顺序时就是一个定值,所有w+s不大于sum_w - risk(假想的答案)的奶牛,假定有k头,可以以任意顺序放在最后一个,进而可以扩展到这k头可以以任意顺序放在最后k个位置;不妨以w+s从大到小顺序放置,之后sum_w的值减小,又有k2头奶牛w+s不大于sum_w2 - risk,如法炮制。所以,按w+s排序即可代表最优顺序

1 #include <algorithm>
2 #include <cmath>
3 #include <cstdio>
4 #include <cstdlib>
5 using namespace std;
6 #define ll long long
7
8 const int maxn = 5e4 + 5;
9 int n;
10
11 struct cow {
12 ll w, s;
13 bool operator<(const cow& o) const { return w + s > o.w + o.s; }
14 } a[maxn];
15
16 int main() {
17 scanf("%d", &n);
18 for (int i = 0; i < n; i++) scanf("%lld %lld", &a[i].w, &a[i].s);
19 sort(a, a + n);
20 ll sum = 0, risk = (int)-1e9;
21 for (int i = 0; i < n; i++) sum += a[i].w;
22 for (int i = 0; i < n; i++) {
23 risk = max(risk, sum - a[i].w - a[i].s);
24 sum -= a[i].w;
25 }
26 printf("%lld\n", risk);
27 }
最大化平均值
POJ 2976 裸的01分数规划

1 #include <algorithm>
2 #include <cmath>
3 #include <cstdio>
4 #include <cstring>
5 #include <iostream>
6 #include <vector>
7 using namespace std;
8 #define ll long long
9 const int N = 1e3 + 5;
10
11 int n, k;
12 double a[N], b[N], c[N];
13
14 bool C(double x) {
15 for (int i = 0; i < n; i++) {
16 c[i] = a[i] - b[i] * x;
17 }
18 sort(c, c + n, greater<double>());
19 double res = 0;
20 for (int i = n - k - 1; i >= 0; i--) {
21 res += c[i];
22 }
23 return res >= 0;
24 }
25
26 int main() {
27 while (scanf("%d%d", &n, &k) != EOF, n) {
28 for (int i = 0; i < n; i++) scanf("%lf", &a[i]);
29 for (int i = 0; i < n; i++) scanf("%lf", &b[i]);
30 double l = 0, r = 1e12 + 1;
31 for (int i = 0; i < 60; i++) {
32 double mid = (l + r) / 2;
33 if (C(mid))
34 l = mid;
35 else
36 r = mid;
37 }
38 printf("%.0f\n", l * 100);
39 }
40 }
POJ 3111 同上

1 #include <algorithm>
2 #include <cmath>
3 #include <cstdio>
4 #include <cstring>
5 #include <iostream>
6 #include <vector>
7 using namespace std;
8 #define ll long long
9 const int maxn = 1e5 + 5;
10
11 int n, k;
12 double a[maxn], b[maxn];
13
14 struct node {
15 double c;
16 int id;
17 bool operator<(const node &o) const { return c > o.c; }
18 } p[maxn];
19
20 bool C(double x) {
21 for (int i = 0; i < n; i++) {
22 p[i].c = a[i] - b[i] * x;
23 p[i].id = i + 1;
24 }
25 sort(p, p + n);
26 double res = 0;
27 for (int i = 0; i < k; i++) res += p[i].c;
28 return res >= 0;
29 }
30 int main() {
31 scanf("%d %d", &n, &k);
32 for (int i = 0; i < n; i++) scanf("%lf %lf", &a[i], &b[i]);
33 double l = 0, r = 1e6 + 1;
34 for (int i = 0; i < 60; i++) {
35 double mid = (l + r) / 2;
36 if (C(mid)) {
37 l = mid;
38 } else
39 r = mid;
40 }
41 for (int i = 0; i < k; i++)
42 printf("%d%c", p[i].id, i == k - 1 ? '\n' : ' ');
43 }
查找第k大的值
POJ 3579 给出N个数,有C(n, 2)个差值,输出这些差值的中位数(偶数个时输出偏小的一个)
二分答案。这题貌似卡常,二分的右端点得结合实际数据a[n-1]-a[0],直接1e9会T

1 #include <algorithm>
2 #include <cmath>
3 #include <cstdio>
4 #include <iostream>
5 using namespace std;
6 #define ll long long
7
8 const int maxn = 1e5 + 5;
9 ll a[maxn], m;
10 int n;
11
12 bool C(int x) {
13 ll sum = 0;
14 for (int i = 0; i < n; i++) {
15 sum += upper_bound(a + i + 1, a + n, a[i] + x) - (a + i + 1);
16 }
17 return sum >= m;
18 }
19
20 int main() {
21 while (scanf("%d", &n) != EOF) {
22 m = ((ll)n * (n - 1) / 2 + 1) / 2;
23 for (int i = 0; i < n; i++) scanf("%lld", &a[i]);
24 sort(a, a + n);
25 int l = -1, r = a[n - 1] - a[0];
26 while (r - l > 1) {
27 int mid = l + (r - l) / 2;
28 if (C(mid)) {
29 r = mid;
30 } else {
31 l = mid;
32 }
33 }
34 printf("%d\n", r);
35 }
36 }
POJ 3658 有一个N(<=5e4)阶矩阵,第i行第j列元素大小为 i2 + 100000 × i + j2 - 100000 × j + i × j,求第M小的数
好题目。j 固定时式子随 i 递增,所有二分judge时可以枚举 j ,再次二分得出这一列有多少个不大于x的数
事实上矩阵在这题没有起到作用,是在提示我们考虑固定i或者j来考虑

1 #include <algorithm>
2 #include <cmath>
3 #include <cstdio>
4 #include <iostream>
5 using namespace std;
6 #define ll long long
7
8 ll n, m;
9
10 int c1(ll x, ll b, ll c) {
11 if (1 + b + c > x) return 0;
12 int l = 0, r = n, ans;
13 while (r >= l) {
14 ll mid = (l + r) / 2;
15 if (mid * mid + b * mid + c <= x) {
16 l = mid + 1;
17 ans = mid;
18 } else
19 r = mid - 1;
20 }
21 return ans;
22 }
23
24 bool c2(ll x) {
25 ll sum = 0;
26 for (ll i = 1; i <= n; i++) sum += c1(x, 100000 + i, i * i - 100000 * i);
27 return sum >= m;
28 }
29
30 int t;
31 int main() {
32 scanf("%d", &t);
33 while (t--) {
34 scanf("%lld %lld", &n, &m);
35 ll l = (ll)-3e9, r = (ll)8e9, ans;
36 while (r >= l) {
37 ll mid = l + (r - l) / 2;
38 if (c2(mid)) {
39 r = mid - 1;
40 ans = mid;
41 } else
42 l = mid + 1;
43 }
44 printf("%lld\n", ans);
45 }
46 }
最小化第k大的值
POJ 2010 C个cow,有分数和学校要给它们的补贴,选取N(奇数)个,使得总补贴不超过F且分数的中位数尽可能大。在2.4节已经出现过这题了
按分数排序。考虑下标id,其意义是 分数前一半(含中位数)的下标都大于等于id,后一部分是从未选cow中按补贴选(可能分数也大于中位数),注意 下标恰好为id的cow不一定选!不这样选是没有单调性的,无法二分
这题其实没有二分的必要,judge就已经是nlogn的,而原先用堆维护答案扫一遍过去也是nlogn。事实上维护堆是200ms,二分是500ms

1 #include <algorithm>
2 #include <cstdio>
3 #include <iostream>
4 #include <vector>
5 using namespace std;
6 #define ll long long
7 #define pii pair<int, int>
8 #define fi first
9 #define se second
10 #define pb push_back
11
12 const int maxn = 1e5 + 5;
13
14 pii cow[maxn];
15 int t[maxn];
16 int n, c, f;
17
18 inline bool C(int id) {
19 ll sum = 0;
20 for (int i = id; i < c; i++) t[i] = cow[i].se;
21 sort(t + id, t + c);
22 for (int i = id; i <= id + n / 2; i++) sum += t[i];
23 vector<int> vec;
24 for (int i = id + n / 2 + 1; i < c; i++) vec.push_back(t[i]);
25 for (int i = 0; i < id; i++) vec.push_back(cow[i].se);
26 sort(vec.begin(), vec.end());
27 for (int i = 0; i < n / 2; i++) sum += vec[i];
28 // printf("%d %d\n", id, sum);
29 return sum <= f;
30 }
31
32 int main() {
33 scanf("%d %d %d", &n, &c, &f);
34 for (int i = 0; i < c; i++) scanf("%d %d", &cow[i].fi, &cow[i].se);
35 sort(cow, cow + c);
36
37 int l = n / 2, r = c - n / 2 - 1, ans = -1;
38 while (r >= l) {
39 int mid = (l + r) / 2;
40 if (C(mid)) {
41 l = mid + 1;
42 ans = mid;
43 } else
44 r = mid - 1;
45 }
46
47 printf("%d\n", ans == -1 ? -1 : cow[ans].first);
48 }
POJ 3662 给个电线连接图,供电公司可以免费提供k条电线,自己需要支付的费用是剩余电线中最长的长度。要使节点1到N联通,求最小费用
二分最小费用,小于等于x的边全选,长度视为0;否则为1,最后d[N]就代表需要供电公司提供的电线数量,与k比较
这一题的条件可以更隐晦成,“使一条路径上第k+1大的线段最小”

1 #include <algorithm>
2 #include <cmath>
3 #include <cstdio>
4 #include <iostream>
5 #include <queue>
6 #include <vector>
7 using namespace std;
8 #define ll long long
9 #define pii pair<int, int>
10 #define fi first
11 #define se second
12 #define pb push_back
13
14 const int maxn = 1e3 + 5;
15 const int inf = 0x3f3f3f3f;
16 int n, m, u, v, x, k;
17
18 struct edge {
19 int to, cost;
20 };
21 vector<edge> g[1005];
22 int d[maxn], vis[maxn];
23
24 bool C(int x) {
25 for (int i = 1; i <= n; i++) {
26 d[i] = inf;
27 vis[i] = 0;
28 }
29 priority_queue<pii, vector<pii>, greater<pii> > que;
30 d[1] = 0;
31 que.push(pii(0, 1));
32 while (!que.empty()) {
33 pii p = que.top();
34 que.pop();
35 int v = p.se;
36 if (d[v] < p.fi) continue;
37 vis[v] = 1;
38 for (int i = 0; i < g[v].size(); i++) {
39 edge e = g[v][i];
40 int l = e.cost <= x ? 0 : 1;
41 if (!vis[e.to] && d[e.to] > l + d[v]) {
42 d[e.to] = l + d[v];
43 que.push(pii(d[e.to], e.to));
44 }
45 }
46 }
47 return d[n] <= k;
48 }
49
50 int main() {
51 scanf("%d %d %d", &n, &m, &k);
52 for (int i = 0; i < m; i++) {
53 scanf("%d %d %d", &u, &v, &x);
54 g[u].pb(edge{v, x});
55 g[v].pb(edge{u, x});
56 }
57 int l = 0, r = (int)1e6, ans = -1;
58 while (r >= l) {
59 int mid = (l + r) / 2;
60 if (C(mid)) {
61 r = mid - 1;
62 ans = mid;
63 } else
64 l = mid + 1;
65 }
66 printf("%d\n", ans);
67 }
其他
POJ 1759 有一条受重力影响的具有N个节点的线,除了最左右的节点 每个节点的高度为左右节点的高度的平均值-1,给出左节点的高度,求右节点的最低高度,使得所有节点高度都非负
设第二个节点高度为a1,递推关系会发现B的高度与a1成正比,所以二分B点高度可以变为二分a1的高度

1 #include <algorithm>
2 #include <cmath>
3 #include <cstdio>
4 #include <iostream>
5 using namespace std;
6 #define ll long long
7
8 int n;
9 double a;
10
11 bool C(double x) {
12 double a0 = a, a1 = x, a2;
13 for (int i = 3; i <= n; i++) {
14 a2 = 2 * a1 - a0 + 2;
15 if (a2 < 0) return false;
16 if (a2 >= a1) return true;
17 a0 = a1, a1 = a2;
18 }
19 return true;
20 }
21
22 int main() {
23 scanf("%d %lf", &n, &a);
24 double l = 0, r = a;
25 for (int i = 0; i < 1000; i++) {
26 double mid = (l + r) / 2;
27 if (C(mid))
28 r = mid;
29 else
30 l = mid;
31 }
32 double a0 = a, a1 = r, a2;
33 for (int i = 3; i <= n; i++) {
34 a2 = 2 * a1 - a0 + 2;
35 a0 = a1, a1 = a2;
36 }
37 printf("%.2f", a2);
38 }
POJ 3484 给出n个数列,已知有至多一个数出现了奇数次,找出这个数及其出现次数
考虑前缀和,在出现这个“出现了奇数次的数”之前,所有数的个数是偶数,而在这个数之后,就变成了奇数;以此为依据就可以进行二分
这题输入也不容易,想着gets现在(2020年)已经不提倡用了,就把网上找的代码改成了用cin.getline读取

1 #include <algorithm>
2 #include <cmath>
3 #include <cstdio>
4 #include <cstring>
5 #include <iostream>
6 using namespace std;
7 #define ll long long
8
9 const int maxn = 2e4 + 5;
10
11 int a[maxn], b[maxn], d[maxn];
12 int top;
13
14 char str[100];
15
16 ll count(ll x) {
17 ll sum = 0;
18 for (int i = 0; i < top; i++) {
19 if (x >= b[i])
20 sum += (b[i] - a[i]) / d[i] + 1;
21 else if (x >= a[i])
22 sum += (x - a[i]) / d[i] + 1;
23 }
24 return sum;
25 }
26
27 int main() {
28 ios::sync_with_stdio(false);
29 cin.tie(0);
30 while (cin.getline(str, 100)) {
31 top = a[0] = 0;
32 sscanf(str, "%lld %lld %lld", &a[top], &b[top], &d[top]);
33 top++;
34 if (a[0] == 0) continue;
35 while (cin.getline(str, 100) && strlen(str)) {
36 sscanf(str, "%lld %lld %lld", &a[top], &b[top], &d[top]);
37 top++;
38 }
39 ll l = 0, r = 1LL << 32;
40 while (r - l > 1) {
41 ll mid = l + (r - l) / 2;
42 if (count(mid) & 1)
43 r = mid;
44 else
45 l = mid;
46 }
47 if (r == 1LL << 32)
48 printf("no corruption\n");
49 else
50 printf("%lld %lld\n", r, count(r) - count(r - 1));
51 }
52 }
END
来源:https://www.cnblogs.com/hs-zlq/p/12241895.html
