不光是查找值!“二分搜索”
最大化最小值
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