- 二分最重要的两段性,即数据可以按照某种标准区分开,而不是单调性
- 如果我们要找红色的边缘点
- mid = (l+r)/2 取中间值;
- 如果中间值符合红色的性质,则说明红色的边缘点在[mid,r]范围里,更新l = mid, mid有可能是我们要找的边缘点;
- 如果中间值不符合红色性质,则说明点在[l, mid -1]里,更新r = mid -1.
- 如果我们要绿色的边缘点
- mid = (l + r)/2取中间值
- 如果中间值符合绿色的性质,则说明绿色的边缘点在[l,mid]范围里,更新r = mid, mid有可能是我们要找的边缘点
- 如果中间值不符合绿色的性质,则绿色的边缘点在[mid - 1, r],更新l = mid -1.
- 总结:
- 先写一个check函数;
- 判定在check的情况下(true和false的情况下),如何更新区间:
- 在check(m)==true的分支下是:
- 如果是l=mid,中间值的更新方式是m=(l+r+1)/2
- 如果是r=mid,中间值的更新方式是m=(l+r)/2
#include <iostream>
using namespace std;
const int N = 10000010;
int n, m;
int q[N];
int main()
{
// 数组长度为n
// m个查询
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;
// 1. 去找左半边;check() == q[mid] >= x
while (l < r)
{
int mid = l + r >> 1;
//1.1 满足条件说明去左边找 在[l,mid]里
//1.2 mid是有可能取到答案的
if (q[mid] >= x) r = mid;
//1.3 当前值 < x,说明要找的值在右边,更新左侧边界
else l = mid + 1;
}
// 2.1不存在x,我们找到的是第一个 >= x的数
// 2.2停止的时候l = r?
if(q[l] != x ) cout<<"-1 -1"<<endl;
else
{
cout<< l << ' ';
//3.1 去找右边,最右边的x,去分[mid,r]
// [l,mid-1] [mid, r]
int l = 0, r = n - 1;
while (l < r)
{
int mid = l + r + 1>> 1;
//3.2 check(mid)的定义与mid在哪边息息相关
if (q[mid] <= x) l = mid ;
else r = mid - 1;
}
cout<< r << endl;
}
}
return 0;
}