//在 a 数组中寻找第一个>=x 的数,并返回其下标intbsearch_1(int l, int r){
while (l < r) {
int mid = l + r >> 1; //或写成 (l+r)/2if (a[mid] >= x) r = mid;
else l = mid + 1;
}
return l; //因为 while 循环当 l==r 时终止,return r 也可以
}
尽量向右找目标
//在 a 数组中寻找最后一个<=x 的数,并返回其下标intbsearch_2{
(l < r) {
mid = l + r + >> ;
(a[mid] <= x) l = mid;
r = mid - ;
}
l;
}
(int l, int r)
while
int
1
1
//或写成 (l+r+1)/2
if
else
1
return
//因为 while 循环当 l==r 时终止,return r 也可以
2.2.while(l<=r)
尽量向左找目标
//在 a 数组中寻找第一个>=x 的数,并返回其下标intbsearch_1(int l, int r){
int res = 0x3f3f3f3f; //可以先将 res 设置为一个很大的数,如果 while 循环终止后 res 没变,便没找到while (l <= r) {
int mid = l + r >> 1; //或写成 (l+r)/2if (a[mid] >= x) {
r = mid - 1;
res = mid;
} else {
l = mid + 1;
}
}
return res;
}
尽量向右找目标
//在 a 数组中寻找最后一个<=x 的数,并返回其下标intbsearch_2(int l, int r){
int res = 0x3f3f3f3f; //可以先将 res 设置为一个很大的数,如果 while 循环终止后 res 没变,便没找到while (l <= r) {
int mid = l + r >> 1; //或写成 (l+r)/2if (a[mid] <= x) {
l = mid + 1;
res = mid;
} else {
r = mid - 1;
}
}
return res;
}
3. 为什么当循环条件为 l<=r 时要用 res 来记录答案,为 l<r 时直接返回 l 或 r 即可?
因为当循环条件为 l<r 时,在循环的过程中,可以发现当 mid 满足条件时 l 和 r 的更新方式都是 l=mid、r=mid,而不是 l=mid+1、r=mid-1,在这个过程中 l/r 已经替代 res 完成了记录并更新答案的过程,便不需要额外再用一个变量来存储答案,当循环终止时 l==r,此时的 l/r 就在答案位置,直接输出 l/r 即可;而当循环条件为 l<=r 时,每次 mid 满足条件 l 和 r 都会更新到 mid 的下一位置,所以此时就需要一个 res 变量存储满足条件的位置(mid)并不断更新,res 最后一次更新的值便是答案,并且 while(l<=r) 终止时 l 和 r 不相等,不方便通过 l 和 r 返回答案。
#include<iostream>usingnamespace std;
constint N = 1000010;
int a[N], x, q, n;
intmain(){
cin >> n >> q;
for (int i = 1; i <= n; i++) cin >> a[i];
while (q--) {
cin >> x;
int l = 1, r = n; //左右边界while (l < r) //因为是找第一次出现的位置,那就是尽量往左来,就用模板 1
{
int mid = l + r >> 1;
if (a[mid] >= x) r = mid; //判断条件,如果值大于等于目标值,说明在目标值右边,尽量往左来else l = mid + 1;
}
if (a[l] != x) { //如果找不到这个值
cout << -1 << ' ';
continue;
}
cout << l << " ";
}
return0;
}
(二)P1102 A-B 数对
思路:给出了 C,我们要找出 A 和 B。我们可以遍历数组,即让每一个值先变成 B,然后二分找对应的 A 首次出现位置,看是否能找到。
如果找到 A,那就二分找最后出现的位置,继而,求出 A 的个数,即数对的个数。
#include<iostream>#include<algorithm>usingnamespace std;
constint N = 200010;
longlong a[N], n, c, res, st;
intmain(){
cin >> n >> c;
for (int i = 1; i <= n; i++) cin >> a[i];
sort(a + 1, a + 1 + n); //先排序for (int i = 1; i < n; i++) //遍历每一个 B
{
int l = i + 1, r = n; //寻找 A 第一次出现的位置,使得 A-B=Cwhile (l < r) //因为是第一次出现,尽量往左找
{
int mid = l + r >> 1;
if (a[mid] - a[i] >= c) r = mid; //判断:在目标值的右边,满足,往左来else l = mid + 1;
}
if (a[l] - a[i] == c) st = l; //能找到 C 就继续elsecontinue;
l = st - 1, r = n; //查找 A 最后出现的位置while (l < r) //因为是最后一次出现,尽量往右找
{
int mid = l + r + 1 >> 1;
if (a[mid] <= a[st]) l = mid; //判断:在目标值的左边,满足,往右去else r = mid - 1;
}
res += l - st + 1; //最后出现的位置减首次出现的位置就是区间长度,即 A 的个数
}
cout << res;
return0;
}
设计 check 函数
可以看出来这题是尽量向右(向上)找答案,这道题我们可以用一个变量 sum 存储得到的总长度,如果 sum 大于等于 m 就直接返回 true,否则返回 false.
套模板
#include<iostream>usingnamespace std;
constint N = 1e6 + 10;
int n, m, a[N];
boolcheck(int x){
int sum = 0;
for (int i = 1; i <= n; i++) {
if (a[i] > x) sum += a[i] - x;
if (sum >= m) returntrue;
}
returnfalse;
}
intmain(){
cin >> n >> m;
int height = 0;
for (int i = 1; i <= n; i++) {
cin >> a[i];
height = max(height, a[i]); //记录最大高度
}
int l = 0, r = height;
while (l < r) {
int mid = l + r + 1 >> 1;
if (check(mid)) //判断是否达到要求的木材总量 m,更新区间
l = mid;
else r = mid - 1;
}
cout << l << endl;
return0;
}