【基础算法】二分


对于二分算法来说,二分是一定会找到答案的。因为二分不断压缩区间,最后必定会分出一个值。
但是这个答案仅仅是对于二分这个算法求出的答案。答案是否正确,还是要结合题目判断。简约的说就是二分的答案不一定是题目的答案,但是二分一定会有答案,无解是对于问题的。

整数二分

算法模板

模板1:区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:

bool check(int x) {/* ... */} // 检查x是否满足某种性质

int bsearch_1(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (check(mid)) r = mid;    // check()判断mid是否满足性质
        else l = mid + 1;
    }
    return l;
}

模板2:区间[l, r]被划分成[l, mid - 1]和[mid, r]时使用:

int bsearch_2(int l, int r)
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1;
        if (check(mid)) l = mid;
        else r = mid - 1;
    }
    return l;
}

总结:模板 1 找 区间左边界点,模板 2 找 区间右边界点。
在这里插入图片描述
例如模板1在上图中找的就是 第一个 3,左端点 ;模板2找的是 最后一个3,右端点 。

模板题 数的范围

题目
给定一个按照升序排列的长度为 n 的整数数组,以及 q 个查询。
对于每个查询,返回一个元素 k 的起始位置和终止位置(位置从 0 开始计数)。
如果数组中不存在该元素,则返回 -1 -1。
输入格式
第一行包含整数 n 和 q,表示数组长度和询问个数。
第二行包含 n 个整数(均在 1 ∼ 10000 范围内),表示完整数组。
接下来 q 行,每行包含一个整数 k,表示一个询问元素。
输出格式
共 q 行,每行包含两个整数,表示所求元素的起始位置和终止位置。
如果数组中不存在该元素,则返回 -1 -1。
数据范围
1 ≤ n ≤ 100000
1 ≤ q ≤ 10000
1 ≤ k ≤ 10000
输入样例
6 3
1 2 2 3 3 4
3
4
5
输出样例
3 4
5 5

想要找到目标值 x 的起始坐标,可理解成找到 ≥x 的最小值,再判断找到的边界值是否与 x 相等,若不相等返回 -1;同样,想要找到目标值 x 的终止坐标,可理解成找到 ≤x 的最大值,再判断找到的边界值是否与 x 相等,若不相等返回 -1;

#include <iostream>
using namespace std;
const int N = 100010;
int n, m;
int q[N];

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i ++ ) scanf("%d", &q[i]);
    while (m -- )
    {
        int x;
        scanf("%d", &x);

        int l = 0, r = n - 1;
        while (l < r)
        {	//找左端点
            int mid = l + r >> 1;
            if (q[mid] >= x) r = mid;
            else l = mid + 1;
        }
        //二分必定会找到答案,但答案可能不符合题意
		//这里二分得出的答案不等于x就是错误的,直接输出-1返回
        if (q[l] != x) cout << "-1 -1" << endl;
        else
        {
            cout << l << ' ';
            int l = 0, r = n - 1;
            while (l < r)
            {	//找右端点
                int mid = l + r + 1 >> 1;
                if (q[mid] <= x) l = mid;
                else r = mid - 1;
            }
            cout << l << endl;
        }
    }
    return 0;
}

浮点数二分

浮点数二分没有整数,每次区间长度一定可以严格缩小一半,不需要处理边界,一定要时时刻刻保证答案在区间内部,一开始可能在 l ~ r,每次通过中间点判断答案落在哪一半边,只要保证每一次答案落在区间中就可以了,当区间长度很小的时候,就可以认为我们找到了答案
比如当整个浮点数二分区间的长度 r - l 已经 ≤ 10^-6(就可以把它当做一个数了),可以用 l 或者 r 当做答案

算法模板

bool check(double x) {/* ... */} // 检查x是否满足某种性质
double bsearch_3(double l, double r)
{
    const double eps = 1e-6;   // eps 表示精度,取决于题目对精度的要求
    while (r - l > eps)
    {
        double mid = (l + r) / 2;
        if (check(mid)) r = mid;
        else l = mid;
    }
    return l;
}

不用精度来表示迭代,直接循环 100 次( 相当于把整个区间的长度 / 2^100 )也可以
循环 n 次,相当于把整个区间的长度 / 2^n

模板题 数的三次方根

给定一个浮点数n,求它的三次方根。
输入格式
共一行,包含一个浮点数n。
输出格式
共一行,包含一个浮点数,表示问题的解。
注意,结果保留6位小数。
数据范围
−10000≤n≤10000
输入样例
1000.00
输出样例
10.000000

经验值:
当结果保留6位小数时,r-l = 1e-8;
当结果保留5位小数时,r-l = 1e-7;
往后多开两位

#include <iostream>
using namespace std;
int main()
{
    double x;
    cin>>x;
    double l = -10000, r = 10000;
    while(r - l >=1e-8)
    {
        double mid = (l + r )/2;
        if(mid*mid*mid>=x) r = mid;
        else l = mid;
    }
    printf("%lf",l);
    return 0;
}

相关推荐

  1. 算法| ss

    2024-07-21 03:26:02       29 阅读
  2. 算法-

    2024-07-21 03:26:02       27 阅读
  3. 算法·

    2024-07-21 03:26:02       15 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-21 03:26:02       52 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-21 03:26:02       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-21 03:26:02       45 阅读
  4. Python语言-面向对象

    2024-07-21 03:26:02       55 阅读

热门阅读

  1. git rebase 和 git merge区别

    2024-07-21 03:26:02       16 阅读
  2. go语言的基础语法

    2024-07-21 03:26:02       16 阅读
  3. 华为OD机试(C卷+D卷)2024真题目录

    2024-07-21 03:26:02       18 阅读
  4. docker 安装 使用 ubuntu

    2024-07-21 03:26:02       20 阅读
  5. Eureka在Kubernetes中的部署指南:微服务发现的艺术

    2024-07-21 03:26:02       15 阅读
  6. 栈的概念—函数调用

    2024-07-21 03:26:02       16 阅读