『学习笔记』二分算法
今天记录二分知识点。
二分是一个简单清晰,实用性强的算法。
也是本人最喜欢的算法之一。
先给出二分模板吧!
int l = 1, r = n;
//初始值,根据情况而定
while (l + 1 < r) {
int mid = (l + r) >> 1;
if (check(mid)) l = mid;
// check函数判断左半部分是否不符合,更新 l
else r = mid;
// 否则更改 r
}
Part one: 二分搜索
二分是一种高效的查找方法,它会将搜索范围一分为二,减小搜索范围,直到找到目标或确定目标不存在。一般可以将时间复杂度从
但是,二分有个前提要求,就是应具有单调性,比如单调不递减(升序)序列。
大家想到的第一种方法应该是遍历整个序列找
这时,大名鼎鼎的二分就能发挥其作用了。
先看一段代码:
scanf("%d", &x); // 查找的数字
stable_sort(a + 1, a + n + 1);
// 排序,使序列具有单调性,才能二分
int l = 1, r = n;
while (l + 1 < r) {
int mid = (l + r) >> 1;
if (a[mid] <= x) l = mid;
else r = mid;
}
pos = l;
printf("%d", pos);
二分的第一步就是确定查找的范围,即
在序列中二分时,
二分时,首先找出中间值,即 mid = (l + r) >> 1
,等价于 mid = (l + r) / 2
。
然后,我们需要更新左右端点的值。
若
否则,缩小范围,既然左边满足条件,我们只需关注左边的部分,将右端点(
此时,区间就缩小到
之后不断如此操作,就能将时间复杂度大大减少。
看模板,此时的 a[mid] <= x
。
Part two:二分答案
什么是二分答案呢?顾名思义,我们二分的不再是坐标,而是此题的答案。
同样重要的是单调性!答案具有单调性,才能使用二分大法。
再次看到二分模板。
int l = 1, r = n;
while (l + 1 < r) {
int mid = (l + r) >> 1;
if (check(mid)) l = mid;
else r = mid;
}
此时,二分中的
是不是很 easy 呢?光说不练假本事,上道例题。
luogu P2440
题目大意人话:
使
不难发现,随着
想想 check
函数怎么写。
没错,记录当
如此代码:
inline bool check(int x) {
int ret = 0;
for (int i = 1; i <= n; ++i)
ret += a[i] / x;
return ret >= k;
}
如果满足,由于求满足条件的最大值,
否则,同理,将右端点更新为
最后答案便储存在
你的第一道二分题就
#include <bits/stdc++.h>
#define ll long long
#define pii pair <int, int>
using namespace std;
const int N = 1e5 + 10;
int a[N], n, k;
inline bool check(int x) {
int ret = 0;
for (int i = 1; i <= n; ++i)
ret += a[i] / x;
return ret >= k;
}
int main() {
scanf("%d%d", &n, &k);
int l = 0, r = 1;
//注意有无解情况,l的初始值为0。
for (int i = 1; i <= n; ++i) {
scanf("%d", &a[i]);
r = max(r, a[i]);
}
while (l + 1 < r) {
int mid = (l + r) >> 1;
if (check(mid)) l = mid;
else r = mid;
}
printf("%d", l);
return 0;
}
这里有一个题单,同学们一起进步吧~