一般来讲我们会在以下情况用到二分:

  • 求单调函数的零点
  • 求一堆东西的最小值最大是多少
  • 很难直接算出答案,但是很好判定答案合不合法

如果想学就继续看吧!

二分查找

二分是一种可以再\(\mathcal{O}(\mathrm{ch}\log m)\)\(m\)为数据规模,\(\mathrm{ch}\)为判断状态合法性check()函数时间复杂度)时间复杂度内求解问题的方法,主要是一个分治,每次将搜索范围减半。

我们以二分查找引入。

有一个有序序列,二分查找的思想如下:

1.定义L,R,mid代表答案在[L,R]内,中点为mid
2.查找mid所在值,如果mid大于查找值,搜索范围定为[L,mid],如果mid小于查找值,搜索范围定为[mid,R],如果查找到了,则返回L
3.将mid重新设为中点,并调至2

  • 二分查找的时间复杂度是\(\mathcal{O}(\log n)\)
  • 顺序查找的时间复杂度是\(\mathcal{O}(n)\)

现在比较它们两个:

例1:

例2:

代码的话在这:

//二分查找(递归)
int binarySearch(int list[],int left,int right,int number)
{
    if(list==NULL)  return -1;
    int index=0;
    int mid=(right+left)/2;
    if(left>right)
        return -1;
    if(number==list[mid])
    {
        index=mid;
        return index;
    }
    else if(number>list[mid]) binarySearch(list,mid+1,right,number);
    else binarySearch(list,left,mid-1,number);
}
//二分查找(循环)
int binarySearch(int list[],int left,int right,int number)
{
    if(list==NULL) return -1;
    while(left<right)
    {
        int mid=(right+left)/2;
        if (list[mid] == number) return mid;
        else if (number > list[mid]) left=mid+1;
        else if (number < list[mid]) right=mid-1;
    }
    return -1;
}

二分答案

二分答案嘛,就是想二分查找一样,只是判断改了,模板:

while(l<r)
{
    mid=(l+r)/2;
    if (check(mid)) r=mid;
    else l=mid+1;
}

check一般是贪心DP

具体应用

比如方程求根啦:

题目描述

有形如:\(ax^3+bx^2+cx^1+dx^0=0\) 这样的一个一元三次方程。给出该方程中各项的系数(a,b,c,d均为实数),并约定该方程存在三个不同实根(根的范围在\(-100\)\(100\)之间),且根与根之差的绝对值\(≥1\)。要求由小到大依次在同一行输出这三个实根(根与根之间留有空格),并精确到小数点后\(2\)位。

提示:记方程\(f(x)=0\),若存在两个数\(x_1\)\(x_2\),且\(x_1<x_2\)\(f(x_1)\times f(x_2)<0\),则在\((x_1,x_2)\)之间一定有一个根。

输入格式

一行,\(4\)个实数\(A,B,C,D\)

输出格式

一行,\(3\)个实根,并精确到小数点后\(2\)位。

输入输出样例

输入

1 -5 -4 20

输出

-2.00 2.00 5.00

这提示明摆着疯狂暗示我们二分啊,按提示二分即可:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define db double
using namespace std;
db a,b,c,d,f1,f2;
int cnt=0;
db check(db x){return a*x*x*x+b*x*x+c*x+d;}
int main()
{
    cin>>a>>b>>c>>d;
    db l,r,mid;
    for(db i=-100;i<=100;++i)
    {
        f1=check(i);
        f2=check(i+1);
        if(!f1){printf("%.2lf ",i);cnt++;}
        if(f1*f2<0)
        {
            l=i,r=i+1;
            while(r-l>=0.001)
            {
                mid=(l+r)/2;
                if (check(mid)*check(r)>0) r=mid;
                else l=mid;
            }
            printf("%.2lf ",r);
            cnt++;
        }
        if (cnt==3) break;
    }
    return 0;
}

小学奥数啦:

题目描述

甲、乙两人同时从A地出发要尽快同时赶到B地。出发时A地有一辆小车,可是这辆小车除了驾驶员外只能带一人。已知甲、乙两人的步行速度一样,且小于车的速度。问:怎样利用小车才能使两人尽快同时到达。

输入格式

仅一行,三个数据分别表示AB两地的距离s,人的步行速度a,车的速度b。

输出格式

两人同时到达B地需要的最短时间,保留6位小数。

输入输出样例

输入

120 5 25

#### 输出

9.600000

这种题想不出来也可以二分啊。

思路如下

然后二分C点啦:

#include<cstdio>
#include<cmath>
int main()
{
    double s,s1,s2,v1,v2,t1,t2,p;
    double a,b;
    scanf("%lf%lf%lf",&s,&v1,&v2);
    s1=0;
    s2=s;
    do
    {
        p=(s1+s2)/2.0;
        a=p/v2;
        b=(p-a*v1)/(v1+v2);
        t1=a+(s-p)/v1;
        t2=a+b+(s-(a+b)*v1)/v2;
        if(t1<t2)
        	s2=p;
        else
        	s1=p;
    }
    while (fabs(t1-t2)>1e-8);
    printf("%.6lf",t1);
    return 0;
}