整数域上的二分
模板样式
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;
}