【算法-基础】二分答案(Binary Search)

引入

前置知识:二分搜索。

定义

二分答案是在二分搜索基础上扩展的算法。

对于纯的朴素算法,枚举 \(n\) 个可能的答案再进行判断(复杂度 \(O(m)\)),复杂度是 \(O(nm)\) 的,而二分的应用可以将其降低至 \(O(\log n \cdot m)\)

大致的应用条件是:

  • 答案在一个固定的有限区间内;
  • 容易判断一个值是否符合条件;
  • 区间呈现单调性。

具体实现

对照普通的二分查找——我们在判断一个数是否能作为答案时是判断了它的大小,而在二分答案中只需将这种判断改为一个判断函数即可。

与二分查找相同,二分答案也需要在有序数列内进行。

应用

  • 朴素算法优化

通常可以使 \(O(n)\) 的时间复杂度变为 \(O(\log n)\)

  • 最大值最小化/最小值最大化

(二分答案大概就这一个正经用处(((

其实在一个范围内可行的 最大值/最小值 和这个是一个做法。

因为大部分要查找的答案就是这个:

\[\begin{matrix} \Large\text{代价较低} \qquad \qquad \qquad \quad \Large\text{代价较高} \\ \Large\downarrow \qquad \qquad \qquad \qquad \qquad \Large\downarrow \\ \Huge\colorbox{red}{不合法解 } \Huge\colorbox{blue}{合法解 } \\ \qquad \; \Large\uparrow \\ \qquad \; \Large\text{答案} \end{matrix} \]

当当前答案为合法解,就将该值保留在区间内,再向更严格的区间内二分,否则将其排除出区间,向更松弛的区间内二分。

例题 - 月度开支

二分一般是直接对答案二分。

将花费作为二分对象,再判断使用这个花费能否使每个月都足够。

点击查看代码
/*
compiling in hszxoj
standard c++14
ide vscode
g++ test.cpp -o test && ./test
*/
#include <bits/stdc++.h>
#include <bits/extc++.h>
namespace {
#define filein(x) freopen(x".in", "r", stdin)
#define fileout(x) freopen(x".out", "w", stdout)
#define file(x) filein(x), fileout(x)
using namespace std;
using namespace __gnu_pbds;
#define ll long long
#define db double
#define un unsigned
#define ui un int
#define ull un ll
#define udb un db
template <typename T>
using pr = pair<T, T>;
#define pii pair<int>
#define pll pair<ll>
#define pdb pair<db>
#define fir first
#define sec second
#define mp(x, y) make_pair(x, y)
const int man = 1e5+10;
}

int n, m;
int a[man];
bool check (int x) {
    int cnt = 1, p = 0;
    for (int i = 1; i <= n; ++ i) {
        if (p+a[i] > x) ++ cnt, p = 0;
        p += a[i];
        if (cnt>m || a[i]>x) return 0;
    } return 1;
}
void pre () ;
int main () {
    pre();
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++ i) scanf("%d", a+i);
    int l = 1, r = 1e9, mid;
    while (l < r) {
        mid = (l+r)>>1;
        // printf("%d %d |%d\n", l, r, mid);
        if (check(mid)) r = mid; // 由于这个 mid 可行,所以 r 保留它;
        else l = mid+1; // 这个 mid 不可行,所以不保留.
    } printf("%d", (l+r)>>1); // 注意这行:需要最后再重新运算一下 mid.
    return 0;
}

void pre () {
#ifndef ONLINE_JUDGE
    file("test");
#endif
    return ;
}
// --- 
posted @ 2023-12-26 21:41  STA_Morlin  阅读(369)  评论(0)    收藏  举报