二分法-数的范围
2021.11.4
单调性---->二分法(不一定要求单调)
找到一个性质,将数列一分为二,用二分法寻找边界
原数列为升序排列好的一组数。
注:本人认为,在解决数的范围这个问题上,传统二分法似乎不是最优解法
代码段注释:
上方两个函数模板为通用二分法模板
主函数内的函数性质为寻找目标元素首次出现和末次出现的下标
在寻找首次出现下标时,首先二分数列,划分定位到目标元素所在的区间段(即if中的判断元素是否大于目标元素)
因为要查找首次出现的下标,必然是先将右边界先赋值,主要查找“相对左边”的一个区间段。
注意到先给右边界赋值这个决定了使用第一模板mid的取值应该为 L+R,考虑到边际效应;
然后在if句不满足的时候,将左边界赋值 丢弃 一定不存在目标数的区间段。
最后当区间压缩为1的时候,则必然能找到一个数字(无论符不符合条件,二分法一定最后压缩出来一个结果,最后再进行性质判断)
然后再是查找末次出现下标,再次二分数列,这次主要查找“相对右边”的一个区间段,所以必然先给左边界赋值(这个左边界第一次赋值一定是第一次出现),不满足if条件时,将右边界赋值 丢弃一定不存在目标数的区间段,直到区间压缩为1,即是目标数。
这里的给左边界先赋值决定了使用第二模板即mid=L+R+1;
int bsearch1(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;
}
int bsearch2(int l,int r)
{
while (l < r)
{
int mid = l + r+1 >> 1;
if (check(mid)) l= mid;
//check判断mid是否满足性质
else r = mid - 1;
}
return l;
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(0);
cin >> n >> m;
for (int i = 0; i < n; i++)cin >> a[i];
while (m--)
{
int x;
cin >> x;
int l = 0, r = n - 1;
while (l < r)
{
int 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 << ' ';
int l = 0, r = n - 1;
while (l < r)
{
int mid = l + r + 1 >> 1;
if (a[mid] <= x)l = mid;
else r = mid - 1;
}
cout << l << endl;
}
}
}
二分法-数的三次方根
2021.11.05
给定一个浮点数,求它的三次方根,并保留6位小数
解析
判断性质为如果中间数的三次方大于x时,则将右边界缩小,所以采用第一个模板mid=r+l/2;
值得注意的是结果需要保留6位小数,这里需要使用一个printf库函数,默认保留小数点后6位
下面是printf的使用规则
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);
}