经典二分模板与例题

经典二分模板

众所周知,二分主要用于二分查找,但二分实际上不仅仅单限于用于查找,二分可分为二分查找与二分答案。我们在此讨论二分应用的大多数情况,问题具有单调性。

  • 二分查找

  • 二分答案

二分查找 O(logn)

二分查找适用于有序的列表,在有序列表中,采用二分查找的方法能快速找到指定目标。

基本模板
模板一:

Problem Description: 在给定的长度为n的严格单调递增的序列中,找出指定数target的下标,保证一定有解

Solve-Code:

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int n,target,a[1000];//a数组存放指定序列
//情况一:
// 在确定mid时,我们需要加1,读者可自行分析,假如当前区间已经变为[2,3],结果为3,此时会一直陷入死循环。
int solve1(){
    int l=0,r=n-1;
    while(l<r){//结束条件
        int mid=l+r+1>>1;
        if(target<a[mid]) r=mid-1;
        else l=mid;
    }
    return l;
}
//情况二:
int solve2(){
    int l=0,r=n-1;
    while(l<r){//结束条件
        int mid=l+r>>1; 
        if(target>a[mid]) l=mid+1;
        else r=mid;
    }
    return l;
}
int main(){
    cin>>n;
    for(int i=0;i<n;i++)
        cin>>a[i]while(cin>>target){
     solve();
    }
 return 0;   
}
模板二

Problem Description: 在给定的长度为n的非严格单调递增的序列中,找出指定数target第一次出现的下标,保证一定有解。

分析:此处target可能多次出现,我们需要求第一次出现的位置,就需要将右边的位置不断减小,即便是找到了等于target的数据,也可能需要继续减小,那么应当如何减小呢?很明显当我们选取情况二的代码时,target<=a[mid] 时,r=mid会持续减小到l==r;(因为若r=mid赋值后已经小于了目标解,那么r未赋值前,一定有target>a[mid],会矛盾)。此时就是我们要找的解。(同理分析查找最后一次出现位置为情况一)

Solve-Code:

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int n,target,a[1000];//a数组存放指定序列
//情况一:可能含重复查找最后一次出现位置
// 在确定mid时,我们需要加1,读者可自行分析,假如当前区间已经变为[2,3],结果为3,此时会一直陷入死循环。
int solve1(){
    int l=0,r=n-1;
    while(l<r){//结束条件
        int mid=l+r+1>>1;
        if(target<a[mid]) r=mid-1;
        else l=mid;
    }
    return l;
}
//情况二:可能含重复,查找第一次出现位置
int solve2(){
    int l=0,r=n-1;
    while(l<r){//结束条件
        int mid=l+r>>1; 
        if(target>a[mid]) l=mid+1;
        else r=mid;
    }
    return l;
}
int main(){
    cin>>n;
    for(int i=0;i<n;i++)
        cin>>a[i]while(cin>>target){
     cout<<solve1()+1<<' '<<solve2()+1<<endl; //注意数组小标从1开始
    }
 return 0;   
}

关于二分查找问题,还有可能出现查找无结果,那么如何判断呢?
既然l是我们查找到的结果,那么 if(a[l]!=target) 不就表明查找失败了吗?!!

参考题目:

查找

二分答案

当答案区域一定的区间时,可以考虑二分答案,此时我们需要一个额外的check函数来查询当前的答案对题目要求的情况,从而决定下一步分法。

主要伪代码:

ElementType l=0,r=max_ans;
while(l+exp<r){exp是我们定义的精度,譬如在浮点数计算过程中,我们不可能保证每次加1不会超出结果。
  	mid=l+r>>1;
 	if(check(mid)(满足条件)) l=mid; //l或者r范围压缩
 	else r=mid; //范围压缩
}

二分答案用处比较多且灵活,使用前请分析其答案是否可以具有单调性,或者答案的分割对问题的解决是否合理。

银行贷款

Problem Description:当一个人从银行贷款后,在一段时间内他(她)将不得不每月偿还固定的分期付款。这个问题要求计算出贷款者向银行支付的利率。假设利率按月累计。
输入格式
三个用空格隔开的正整数。
第一个整数表示贷款的原值,第二个整数表示每月支付的分期付款金额,第三个整数表示分期付款还清贷款所需的总月数。
输出格式
一个实数,表示该贷款的月利率(用百分数表示),四舍五入精确到0.1%。

对于不清楚利率计算的请先查询利率计算。

本题很明显可以用到二分答案(具有单调性)的方法。

当我们所取答案check()值对满足题目要求影响下不断松弛lr;

Solve-Code:

#include<iostream>
#include<cmath>
#include<cstdio>
using namespace std;
int m,n,k;
double check(double x)
{
    double res=0;int i=1;
    while(i<=k){
      res += n/pow(x+1,i);
      i++;
    }
    return res;
}
int main(){
   cin>>m>>n>>k;
   double l=0,r=10,answer=0;
   while(l+0.0001<r){
    double mid=(l+r)/2;
    if(check(mid) >= m)  l=mid;
    else  r=mid;
   }
   printf("%.1lf",l*100);
    return 0;
}
二分的题目较多,读者需要多理解为什么可以二分,以及二分后,对区间范围的更改判断。
二分答案参考:

数列分段 Section II

木材加工

一元三次方程求解

希望对您有所帮助!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值