『学习笔记』二分算法

今天记录二分知识点。

二分是一个简单清晰,实用性强的算法。

也是本人最喜欢的算法之一。

先给出二分模板吧!

	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: 二分搜索

二分是一种高效的查找方法,‌它会将搜索范围一分为二,‌减小搜索范围,‌直到找到目标或确定目标不存在。‌一般可以将时间复杂度从 O(n) 优化到 O(log2n)。‌

但是,二分有个前提要求,就是应具有单调性,比如单调不递减(升序)序列。

edg. 在一个升序序列 s 中找到一个指定值 x。令 n=|s|n106

大家想到的第一种方法应该是遍历整个序列找 x 吧。这种方法的时间复杂度为 O(n),在某些时候并不足够优秀,可能导致 TLE

这时,大名鼎鼎的二分就能发挥其作用了。

先看一段代码:

	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);

二分的第一步就是确定查找的范围,即 lr 的初始值。

在序列中二分时,l 一般是 1r 一般是序列最后一位的下标。

二分时,首先找出中间值,即 mid = (l + r) >> 1,等价于 mid = (l + r) / 2

然后,我们需要更新左右端点的值。

mid 左边的数已经不满足条件,就可以把 mid 左边舍弃,将左端点( l )更新为 mid

否则,缩小范围,既然左边满足条件,我们只需关注左边的部分,将右端点( r )更新为 mid

此时,区间就缩小到 (l,mid)(mid,r)了。

之后不断如此操作,就能将时间复杂度大大减少。

看模板,此时的 check(mid) 函数为: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;
}

此时,二分中的 check 函数便是判断 mid 是否满足要求,然后与二分查找一样,减短 lr 的范围。

是不是很 easy 呢?光说不练假本事,上道例题。

luogu P2440

题目大意人话

使 i=1nail=kl 的最大值,即 lmax

不难发现,随着 l 的不断增大,总和在不断减少。这就是我们梦寐以求的单调性!接下来,我们就可以快乐地写二分了。

想想 check 函数怎么写。

没错,记录当 l=x 时能砍下的总段数 sum,判断是否 sumk 即可。

如此代码:

inline bool check(int x) {
    int ret = 0;
    for (int i = 1; i <= n; ++i)
        ret += a[i] / x;
    return ret >= k;
}

如果满足,由于求满足条件的最大值,mid 左边就没有价值去搜索了,将目光转向右边,即将左端点更新为 mid

否则,同理,将右端点更新为 mid

最后答案便储存在 l 里了。

你的第一道二分题就 Accepted 啦。

image

#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;
}

这里有一个题单,同学们一起进步吧~

posted @   云岚天上飘  阅读(20)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!

阅读目录(Content)

此页目录为空

喜欢请打赏

扫描二维码打赏

了解更多

点击右上角即可分享
微信分享提示