二分模板(整数+浮点数)

整数域上的二分

模板样式

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

// 区间[l, r]被划分成[l, mid]和[mid + 1, r]时使用:
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;
}

// 区间[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;
}

模板应用实例

给定一个按照升序排列的长度为n的整数数组, 返回一个元素k的起始位置和终止位置(位置从0开始计数),如果不存在输出"-1 -1"
以下为3次输入,但3 4 5之前输入的内容是一样的,所以这里只写一遍了
6
1 2 2 3 3 4
3 4 5
对应3 4 5的输出依次为:
3 4
5 5
-1 -1

/**
 * 想要理解下面的代码,核心就在于“边界”二字,这道题目需要处理的是比如一个序列比如1 2 2 3 3 4里面有两个3,我们需要
 * 分别输出第一个3的位置和第二个3的位置,我们在找第一个3时,它实际上就是一个边界,它左边是<它的数,右边是
 * >=它的数,所以我们的判断条件才会是if (a[mid] >= x) 和 else(a[mid] < x),实际上就是根据边界找到它的两个
 * 区间,然后如果当前的mid取在了左区间中那么我们就需要将搜索区间向右收缩即l = mid + 1,如果取在了右区间就需要将搜索区间
 * 向左收缩,即r = mid,之所以一个+1,一个不加,原因在于边界右区间是>=待寻值得数,包含=的数,所以mid可能也是答案
 */
#include <iostream>

using namespace std;

const int N = 1e5 + 10;

int n;
int a[N];

int main()
{
    cin >> n;
    for (int i = 0; i < n; ++ i) cin >> a[i];
    
    int x, l, r, mid;
    cin >> x;
    l = 0, r = n - 1;
    while (l < r)
    {
        mid = l + r >> 1;
        if (a[mid] >= x) r = mid;
        else l = mid + 1;
    }
    
    if (a[l] != x) cout << "-1 -1" << endl;
    else
    {
        cout << l << " ";
        l = 0, r = n - 1;
        while (l < r)
        {
            mid = l + r + 1 >> 1;
            if (a[mid] <= x) l = mid;
            else r = mid - 1;
        }
        cout << l << endl;
    }
}

浮点数二分

模板样式

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

模板应用实例

  • 求平方根
#include <stdio.h>

using namespace std;

int main()
{
    double a;
    scanf("%lf", &a);

    double l = 0, r = a > 1.0 ? a : 1.0;
    const double eps = 1e-6; // eps 表示精度,取决于题目对精度的要求 经验值:当题目要求保留4位小数时取1e-6,5位小数-1e-7,6位小数-1e-8,比位数大2
    while (r - l > eps)
    {
        double mid = (l + r) / 2;
        if (mid * mid >= a) r = mid;
        else l = mid;
    }

    printf("%lf\n", l);
    return 0;
}
  • 求立方根

限定输入值n范围为:−10000 ≤ n ≤ 10000

#include <stdio.h>

using namespace std;

int main()
{
    double n;
    scanf("%lf", &n);
    
    double l = -10000, r = 10000;
    
    const double eps = 1e-8;
    while (r - l > eps)
    {
        double mid = (l + r) / 2;
        if (mid * mid * mid >= n) r = mid;
        else l = mid;
    }
    printf("%.6lf\n", l);
    return 0;
}
上一篇:7-4 近似求PI (10分)


下一篇:EPS应急电源设备的组成和选用