前言
这是一道牛客周赛的C题,题目用到了二分的知识,在这里总结一下。 https://ac.nowcoder.com/acm/contest/120553/C
一、题目描述
大概就是给以你一个数,让你在一个很多个区间找这个数是否在给的区间里面。 整体的思路就是 先把区间左端点排序,然后二分查找最后一个满足大于x的区间左端点。
二、代码分析
1.整体代码
代码如下(示例):
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
struct Interval {
int l, r, idx;
};
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, q;
cin >> n >> q;
vector<Interval> range(n);
for(int i = 0; i < n; i++){
cin >> range[i].l >> range[i].r;
range[i].idx=i+1;
}
sort(range.begin(),range.end(),[](Interval a, Interval b) {
return a.l < b.l;
});
for(int k = 0; k < q; k++){
int x;
cin >> x;
// bool is_ok = false;
// int tmp = 0;
//二分
int left=0;
int right=n-1;
int ans=-1;
while(left<=right){
int mid=(left+right)/2;
if(range[mid].l <= x){
ans=mid;
left=mid+1;
}else{
right=mid-1;
}
}
if(ans!=-1 && range[ans].r>=x && x >= range[ans].l){
cout << range[ans].idx << endl;
}else{
cout << -1 << endl;
}
//使用二维数组枚举就是常规思路,最终结果会超时,所以不能使用
// for(int i = 0; i < n; i++){
// if(x >= range[i][0] && x <= range[i][1]){
// is_ok = true;
// tmp = i;
// break;
// }
// }
// if(is_ok){
// cout << tmp + 1 << endl;
// } else {
// cout << -1 << endl;
// }
}
return 0;
}
2.单独分析
代码如下(示例):
int left=0;
int right=n-1;
int ans=-1;
while(left<=right){
int mid=(left+right)/2;
if(range[mid].l <= x){
ans=mid;
left=mid+1;
}else{
right=mid-1;
}
}
显而易见,二分第一步就要声明左右指针left和right; 然后while(左<=右)也很容易写出; 别忘了声明mid,为两指针相加除二后的位置; 接下来if这里就开始饶了:到底怎么写条件呢?
首先我们得知道,mid指针始终是表示的是我们要找的左端点的可能值,而x是固定死的,他不能动,所以我们的思路应该是用左端点去和x比较,而不是用x去找左端点。然后下面举例理解会好点。
假如x=6,[3,4],[5,6],[8,9]; 因为我们要找的是左端点,所以是在3,5,8中二分; 我们的目标是找x是不是在区间里面,所以我们判断停止的条件一定是: mid指向的值 要小于我们的x。这里就是我们的mid指向的左端点要小于6,你好好想想,如果最后停下的时候mid左端点都大于6了,那我们还找啥,他一定是不在区间里面的。
在这个例子里面体现就是你找到的mid是8,8>6,而6不在左端点为8的区间里面,我们要的mid是5,刚好停在了6的左边,达到了我们二分的目的。
所以判断停下的代码就是
if(range[mid].l <= x){
ans=mid;
}
那又有人要问了:诶这ans=mid;为什么跟在判断后面? 显而易见,在哪停下答案就是多少,mid停止的时候就是我们要的答案,我们要保证mid在x的左边,那就必须在条件判断<=x的时候让mid=ans。
然后就是为什么要left=mid+1,right=mid-1而不是直接让左右指针直接等于mid呢? 有两个数3,4 ,left=0,right=1,mid=(0+1)/2=0,left还是=mid=0...导致死循环。
ok,根据上面的情况我们可以总结出第二种情况 1).第一种情况:
if(左边最后一个要找的数[mid] <= x){
ans=mid;
left=mid+1;
}else{
right=mid-1;
}
2).第二种情况:
if(右边边第一个要找的数[mid] >= x){
ans=mid;
right=mid-1;
}else{
left=mid+1;
}
不要忘记这两种情况的左右指针代码的位置要换一下...
总结
二分其实就是判断这里要纠结一下,具体的情况看是要找值x的 左最后 还是 右第一 满足条件的值。
新手第一次写这种算法的博客,可能说的比较乱,以后会一点点精进的XD

京公网安备 11010502036488号