binary_search 整数二分和浮点数二分 例子

蒟蒻又滑了好多天。。。。。。。。。
愧疚。。。。。

题目来源:Acwing
题目链接1
题目链接2

这是一个整数二分很好的题:

给定一个按照升序排列的长度为n的整数数组,以及 q 个查询。

对于每个查询,返回一个元素k的起始位置和终止位置(位置从0开始计数)。

如果数组中不存在该元素,则返回“-1 -1”。

输入格式 第一行包含整数n和q,表示数组长度和询问个数。

第二行包含n个整数(均在1~10000范围内),表示完整数组。

接下来q行,每行包含一个整数k,表示一个询问元素。

输出格式 共q行,每行包含两个整数,表示所求元素的起始位置和终止位置。

如果数组中不存在该元素,则返回“-1 -1”。

数据范围 1≤n≤100000 1≤q≤10000 1≤k≤10000 输入样例:
6 3
1 2 2 3 3 4
3 4 5
输出样例:
3 4
5 5
-1 -1

首先 这个数组是 单调!(全部单调或者局部单调的题都可以使用二分)
也就是连续的 相当于找一找数的左边界和右边界

#include<iostream>
using namespace std;
const int N=100010;
int a[N];
int main(){
    int n,d;
    cin>>n>>d;
    for(int i=0;i<n;i++) cin>>a[i];
    while(d--){
        int k;
        cin>>k;
        int l=0,r=n-1;
        while(l<r){//              这里找的是左边界
            int mid=l+r>>1;
            if(a[mid]>=k) r=mid;//大了就把右边界往左缩
            else l=mid+1;//小了就把左边界往右缩
        }
        if(a[l]!=k) 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]<=k) l=mid;//小了就把左边界往右缩
                else r=mid-1;//大了就把右边界往左缩
            }
            cout<<l<<endl;
        }
    }
    return 0;   
}

这样写只需要120ms左右嗷 如果换成scanf/printf的话只需要40ms左右

假如我们用暴力的做法

#include<iostream>
using namespace std;
const int N=100010;
int a[N];
int main(){
    int n,q;
    scanf("%d%d",&n,&q);
    for(int i=0;i<n;i++) scanf("%d",&a[i]);
    while(q--){
        int num;
        scanf("%d",&num);
        bool total=false;
        for(int i=0;i<n;i++){
            if(a[i]==num&&total==0){ printf("%d ",i); total=true; break;}
        }
        if(total) for(int i=n-1;i>=0;i--){
            if(total&&a[i]==num){
                printf("%d\n",i);
                break;
            }
        }
        if(!total) printf("-1 -1\n");
    }
    return 0;
}

就会得到TLEEEEEEEEEEEEEEE的结果

然后俺们来看一个 浮点数二分的题:

给定一个浮点数n,求它的三次方根。

输入格式 共一行,包含一个浮点数n。

输出格式 共一行,包含一个浮点数,表示问题的解。

注意,结果保留6位小数。

数据范围 −10000≤n≤10000
输入样例:
1000.00
输出样例:
10.000000

我们可以从令左边界为-10000,右边界为10000,然后互相趋近

#include<iostream>
using namespace std;
double xpp(double x){
    return x*x*x;
}
int main(){
  double a;
  scanf("%lf",&a);
  double l=-10000,r=10000;
  while(r-l>=1e-8){//一般取到精度的下两位
      double mid=(l+r)/2;
      if(xpp(mid)>=a) r=mid;//大了就把右区间往回拉
      else l=mid;//小了就把左区间往右拉
  }
  printf("%.6lf",l);
  return 0;
}

当然喜欢不喜欢printf的也可以用cout配合iomanip里的setprcision fixed来取精度^^

然后还有种快速的做法 就是用cmath里的cbrt函数(排位神器),可以直接开三次方,然后再取个6位小数就可以啦
因为数据不大 时间差距不大 想怎么写就怎么写啦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值