二分算法
二分查找(整数二分)
1.问题1
如何在一个严格单调序列A中找出给定的数 x 。
代码
//二分区间为左闭右闭的[left, right], 传入的初值为[0, n- 1]
int binarySearch(int A[], int left, int right, int x)
{
int mid;
while (left <= right)
{
mid = left + right >> 1;
//mid = left + (right - left) / 2;
if (A[mid] == x) return mid;
else if (A[mid] > x)
right = mid - 1;
else
left = mid + 1;
}
return -1;
}
2.问题2
求出序列中第一个大于等于x的元素的位置L以及第一个大于x的元素的位置R,这样元素x在序列中存在的区间就是左闭右开区间[L, R)。
代码
(1)求出序列中第一个大于等于x的元素的位置。
//二分边界时左闭右闭的[left, right],初值[0, n]
int lower_bound(int A[], int left, int right, int x)
{
int mid;
while (left < right)
{
mid = left + right >> 1;
if (A[mid] >= x)
right = mid;
else
left = mid + 1;
}
return left; //返回夹出来的位置
}
- 注意
1.每次循环的搜索空间是[left, right]左闭右闭,[left, right)左闭右开也可。
3.当left == right时while循环中止,此时搜索空间是[left, left]为空,返回 left 或 right 都可。如果元素存在则返回满足条件的位置,反之返回它应该在的位置。
4.二分的初始区间应当能覆盖到所有可能返回的结果。二分下界是很显然是0,但二分上界是 n 不是 n - 1,当查询的元素比序列中所有元素都大时返回n(即假设它存在,它应该在的位置)。故二分初始区间[left, right] = [0, n]。但二分时在n处的值没实际意义。
(2)求序列中第一个大于x的元素的位置。
//二分边界时左闭右闭的[left, right],初值[0, n]
int upper_bound(int A[], int left, int right, int x)
{
int mid;
while (left < right)
{
mid = left + right >> 1;
if (A[mid] > x)
right = mid;
else
left = mid + 1;
}
return left;
}
3.模板总结
寻找有序序列中第一个满足某条件 C 的元素的位置
如果寻找最后一个满足“条件C”的元素的位置,则可以先求第一个满足“条件!C”
的元素的位置。
//二分区间为左闭右闭的[left, right],初值必须能覆盖解的所有可能取值
int solve(int left, int right)
{
int mid;
while(left < right)
{
mid = left + right / 2;
if (条件成立) //条件成立,第一个满足条件的元素的位置 <=mid
{
right = mid;
}
else //条件不成立,第一个满足该条件的位置>mid
{
left = mid + 1;
}
}
return left;
}
4.模板拓展(区间左开右闭)
//二分区间为左闭右闭的(left, right],初值必须能覆盖解的所有可能取值
int solve(int left, int right)
{
int mid;
while(left + 1 < right)
{
mid = left + right / 2;
if (条件成立) //条件成立,第一个满足条件的元素的位置 <=mid
{
right = mid;
}
else //条件不成立,第一个满足该条件的位置>mid
{
left = mid;
}
}
return left;
}
浮点数二分
1.以精度位循环终止条件
while(r-l<eps)//其中eps是所需要的精度
{
mid = (l+r)/2;
...
}
2.例题
1.计算$\sqrt{2}$的近似值。
const double eps = 1e-5;
double f(double x)
{
return x * x - 2;
}
double calSqrt()
{
double left = 1, right = 2;
double mid;
while (right - left > eps)
{
mid = (right + left) / 2;
if (f(mid) > 0)
right = mid;
else
left = mid;
}
return mid;
}
上述问题实际是这个问题的特例:给定一个定义在[L, R]上的单调函数f(x),求方程f(x) = 0 的根。
const double eps = 1e-5;
double f(double x)
{
return ....;
}
double solve(double L, double R)
{
double left = L, right = R;
double mid;
while (right - left > eps)
{
mid = (right + left) / 2;
if (f(mid) > 0)
right = mid;
else
left = mid;
}
return mid;
}
2.木棒切割问题:给出N根木棒,长度一知,现在切割这些木棒至少得到K根长度相等的木棒,求长度相等的木棒最长是多长。
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 110;
int a[N];
int n, k;
int check(int l)
{
int sum = 0;
for (int i = 0; i < n;i ++)
sum += a[i] / l;
return sum;
}
int solve(int left, int right)
{
int mid;
while (left < right)
{
mid = left + right >> 1;
if (check(mid) < k)
right = mid;
else
left = mid + 1 ;
}
return left - 1;
}
int main()
{
scanf("%d%d", &n, &k);
for (int i = 0; i < n; i ++)
scanf("%d", &a[i]);
sort(a, a + n);
int ans = solve(0, a[n-1] + 1);
printf("%d\n", ans);
return 0;
}
参考资料《算法笔记》
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。