【模板】二分答案法(求最后一个小于等于x的数的位置,求第一个大于等于x的数的位置)

题目:1621: 例7.1(变式) 找数【求最后一个小于等于的数的位置】

http://www.sqyoj.club/problem.php?id=1621

题目:1622: 例7.1 (变式)找数【求第一个大于等于的数的位置】

http://www.sqyoj.club/problem.php?id=1622

【一】

题目:1621: 例7.1(变式) 找数【求最后一个小于等于的数的位置】

http://www.sqyoj.club/problem.php?id=1621

分析:

        用lft表示询问区间的左边界,用rght表示询问区间的右边界,[left,rght]组成询问区间

        初始化lft=1,rght=n   ——这是左闭右闭区间!(重要说明:如果是左闭右开区间,代码是不一样的!!!)

        a[0]=-inf,a[n+1]=inf。

        序列已经按照升序排好,保证了二分的有序性。
        每一次二分:
    ①取区间中间值mid=(lft+rght)/2;
    ②判断a[mid]与x的关系:

         因为求的是最后一个小于等于x的数的位置,所以从a[mid]<=x入手考虑。

         if  a[Mid]<=x,由于序列是升序排列,所以取右半区间,修改左边界lft=mid+1,从而lft左边的值都是小于等于x。这里出现难点,为什么是mid+1,而不是mid,原因在于避免死循环,举例说明,(1+2)/2=1,从而mid始终等于lft,陷入死循环。

          else if a[mid]>x,取左半区间,修改右边界rght=mid-1,从而rght右边的值都大于x。
      重复执行二分操作直到lft>rght。
     下面分析答案的情况:

      最终循环结束时,lft=right+1。

      

 

        而lft左边的都是<=x的,rght右边的都是>x的。

       所以答案就是rght。如果rght=0,说明在序列中没有小于等于x的元素,从而输出-1。

AC代码如下:

//例7.1找数(求最后一个小于等于的数的位置) 
#include<cstdio>
#include<iostream>
using namespace std;
int n,m,a[110005],x,lft,rght,mid;
int main(){
    //输入 
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>a[i];
    //初始化 
    a[0]=-1e9;
     
    for(int i=1;i<=m;i++){
        //输入 
        cin>>x;
        //二分答案法
        lft=1;
        rght=n;
            //左闭右闭区间。
            //这里请注意,如果是左闭右开,代码会不一样的 
        while(lft<=rght){//终止时lft=rght+1 
            mid=(lft+rght)/2;
            if(a[mid]<=x)lft=mid+1;//lft左边一定是小于等于x的 
            else rght=mid-1;//rght右边一定是大于x的
        }
        //输出 
        if(rght!=0)cout<<rght<<endl;
            //因为lft左边一定小于等于x的,
            //而最终rght恰好比lft小1,所以输出rght 
        else cout<<-1<<endl;    
    }   
    return 0; 
}

【二】

题目:1622: 例7.1 (变式)找数【求第一个大于等于的数的位置】

http://www.sqyoj.club/problem.php?id=1622

分析:

        二分说明:

         因为求的是第一个大于等于x的数的位置,所以从a[mid]>=x入手考虑。     

        if  a[Mid]>=x,由于序列是升序排列,所以取左半区间,修改左边界rght=mid-1,从而rght右边的值都大于等于x。

        else if a[mid]<x,取右半区间,修改左边界lft=mid+1,从而lft左边的值都小于x。

       答案说明:

       最终循环结束时,lft=right+1。 

  

       而lft左边的都是<x的,rght右边的都是>=x的。

       所以答案就是lft。如果lft=n+1,说明在序列中没有大于等于x的元素,从而输出-1。

      AC代码:

//例7.1找数(求第一个大于等于的数的位置) 
#include<cstdio>
#include<iostream>
using namespace std;
int n,m,a[110005],x,lft,rght,mid;
int main(){
    //输入 
    cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>a[i];
    //初始化 
    a[0]=-1e9;
    a[n+1]=1e9;
     
    for(int i=1;i<=m;i++){
        //输入 
        cin>>x;
        //二分答案法
        lft=1;
        rght=n;
            //左闭右闭区间。
            //这里请注意,如果是左闭右开,代码会不一样的 
        while(lft<=rght){//终止时lft=rght+1 
            mid=(lft+rght)/2;
            if(a[mid]>=x)rght=mid-1;//rght右边一定是大于等于x的
            else lft=mid+1;//rght左边一定是小于x的
        }
        //输出 
        if(lft<=n)cout<<lft<<endl;
            //因为lft左边一定小于等于x的,
            //而最终rght恰好比lft小1,所以输出rght 
        else cout<<-1<<endl;    
    }   
    return 0; 
}

 

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值