Adva::0x04
Posted Reliauk の 窝
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Adva::0x04相关的知识,希望对你有一定的参考价值。
Adva::0x04 二分
Part 1. 引言
二分法是一种随处可见却非常精妙的算法,基础用法是在单调序列 / 函数中进行查找。当问题的答案具有单调性时,我们可以通过二分将求解转化为判定。
据说,只有 \\(10\\%\\) 的程序员能写对二分(?)。二分的方法多种多样,需要注意边界,取舍,精度等。
Part 2. 二分基本模板
- 最大值最小化
while (l < r) {
int mid = (l + r) >> 1;
if (check(mid)) {
r = mid;
} else {
l = mid + 1;
}
}
- 最小值最大化
while (l < r) {
int mid = (l + r + 1) >> 1;
if (check(mid)) {
l = mid;
} else {
r = mid - 1;
}
}
我们考虑第一种方法无解当 \\(mid = r\\);第二种方法无解当 \\(mid = l\\)。我们可以利用这一性质处理无解的情况。
- 实数域二分
while (l + eps < r) {
double mid = (l + r) / 2;
if (check(mid)) {
r = mid;
} else {
l = mid;
}
}
另外,这种方法有时使用下面的写法精度会更高:
for (int i = 0; i < 100; ++i) {
double mid = (l + r) / 2;
if (check(mid)) {
r = mid;
} else {
l = mid;
}
}
Part 3. 二分答案转化为判定
我们在处理算法问题时,常遇到这种问题:对于最优解 \\(S\\),在 \\(S\\) 的一侧均不合法,而在另一侧均合法。形式化地说:若评分越高越优,则 \\(\\forall x > S\\),都不合法,而 \\(\\forall x \\leq S\\),都有方案使得存在合法方案 \\(x_0 \\geq x\\)。
例题
有 \\(n\\) 本书排成一行,厚度为 \\(a_1 \\sim a_n\\)。
把它们分成连续 \\(m\\) 组,使最大组厚度之和最小。
我们二分最大厚度之和,每次 check
判断能否被分为最多 \\(m\\) 组。
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <stack>
#include <cstring>
#include <climits>
using namespace std;
const int kMaxN = 1e5 + 5;
int n, m, a[kMaxN], r;
bool check(int t) {
int groups = 0, sum = 0;
for (int i = 1; i <= n; ++i) {
if (sum + a[i] <= t) sum += a[i];
else ++groups, sum = a[i];
}
return groups < m;
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
r += a[i];
}
int l = 0;
while (l < r) {
int mid = l + r >> 1;
if (check(mid)) r = mid;
else l = mid + 1;
}
cout << l << endl;
return 0;
}
Best Cow Fences
给定正整数数列 \\(A\\),求一个平均数最大的,长度不小于 \\(L\\) 的子段。
二分答案。判定:是否存在一个长度不小于 \\(L\\) 的子段,平均数不小于二分的值。
一个小 trick:每个数减去二分的值,然后判定有无非负子段和。
我们考虑问题“求一个长度不小于 \\(L\\) 的子段最大和”。
故我们维护前缀和 \\(sum\\) 的前缀最小值即可。
#include <iostream>
#include <cstdio>
#include <vector>
#include <algorithm>
#include <stack>
#include <cstring>
#include <climits>
using namespace std;
const int kMaxN = 1e5 + 5;
int n, f, a[kMaxN];
double b[kMaxN], sum[kMaxN];
bool check(double x) {
for (int i = 1; i <= n; ++i) {
b[i] = a[i] - x;
}
for (int i = 1; i <= n; ++i) {
sum[i] = sum[i - 1] + b[i];
}
double ans = -1e10, minval = 1e10;
for (int i = f; i <= n; ++i) {
minval = min(minval, sum[i - f]);
ans = max(ans, sum[i] - minval);
}
return ans >= 0;
}
int main() {
cin >> n >> f;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
}
double eps = 1e-6, l = -1e6, r = 1e6;
while (l + eps < r) {
double mid = (l + r) / 2;
if (check(mid)) {
l = mid;
} else {
r = mid;
}
}
cout << int(1000 * r) << endl;
return 0;
}
Innovative Business
有 \\(n\\) 个元素,每一对元素之间的大小关系是确定的。你必须通过不超过 \\(10000\\) 次询问,每次询问得知两个数之间的大小关系,来把这 \\(n\\) 个数排好序。
我们考虑已经将 \\(k - 1\\) 个元素排好序,插入第 \\(k\\) 个元素。
于是我们二分 \\(a_k\\) 的位置,所有共 \\(n \\times \\log_2n\\) 次询问。
// Forward declaration of compare API.
// bool compare(int a, int b);
// return bool means whether a is less than b.
class Solution {
public:
vector<int> specialSort(int N) {
vector<int> v; v.push_back(1);
for (int i = 2; i <= N; ++i) {
int l = 0, r = v.size() - 1;
while (l <= r) {
int mid = l + r >> 1;
if (compare(v[mid], i)) {
l = mid + 1;
} else {
r = mid - 1;
}
}
v.push_back(i);
for (int j = v.size() - 2; j > r; --j) swap(v[j], v[j + 1]);
}
return v;
}
};
以上是关于Adva::0x04的主要内容,如果未能解决你的问题,请参考以下文章