Adva::0x04

Posted Reliauk の 窝

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Adva::0x04相关的知识,希望对你有一定的参考价值。

Adva::0x04 二分

Part 1. 引言

二分法是一种随处可见却非常精妙的算法,基础用法是在单调序列 / 函数中进行查找。当问题的答案具有单调性时,我们可以通过二分将求解转化为判定。

据说,只有 \\(10\\%\\)程序员能写对二分(?)。二分的方法多种多样,需要注意边界,取舍,精度等。

Part 2. 二分基本模板

  1. 最大值最小化
while (l < r) {
  int mid = (l + r) >> 1;
  if (check(mid)) {
    r = mid;
  } else {
    l = mid + 1;
  }
}
  1. 最小值最大化
while (l < r) {
  int mid = (l + r + 1) >> 1;
  if (check(mid)) {
    l = mid;
  } else {
    r = mid - 1;
  }
}

我们考虑第一种方法无解当 \\(mid = r\\);第二种方法无解当 \\(mid = l\\)。我们可以利用这一性质处理无解的情况。

  1. 实数域二分
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\\) 的子段最大和”。

\\[\\max_{i - j \\geq L}\\left\\{\\sum{a_k}\\right\\} = \\max_{L \\leq i \\leq n}\\left\\{sum_i - \\min_{0 \\leq j \\leq i - L}\\left\\{sum_j\\right\\}\\right\\} \\]

故我们维护前缀和 \\(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的主要内容,如果未能解决你的问题,请参考以下文章

VSCode自定义代码片段——CSS选择器

谷歌浏览器调试jsp 引入代码片段,如何调试代码片段中的js

片段和活动之间的核心区别是啥?哪些代码可以写成片段?

VSCode自定义代码片段——.vue文件的模板

VSCode自定义代码片段6——CSS选择器

VSCode自定义代码片段——声明函数