二分三分查找

虽然标题说的是二分三分查找但是因为三分目前用的不多就水了

我们先来看二分

二分

二分法是指对于区间[a,b]连续不断f(a)·f(b)<0的函数y=f(x),通过不断地把函数f(x)的零点所在的区间一分为二,使区间的两个端点逐步逼近零点,进而得到零点近似值的方法。

条件可对应理解为查询区间 [ l , r ] 上需满足 f(l)*f(r)<0,mid = ( l + r ) >> 1

f ( mid ) < 0 查询区间更新为 [ l , mid ] ; 

f ( mid ) > 0 查询区间更新为 [ mid , r ] ; 

f ( mid ) = 0 则找到零点

一般当我们看到题目委婉地要求你最大值最小或者最小值最大的时候就可以用二分

课堂例题(1) luogu 1024 一元三次方程

答案在[-100,100]范围内,两个根的差的绝对值>=1,故每一个大小为1的区间里至多有1个解。

这是一个0011模型的题,我们利用二分法不断接近零点,所求得的极小区间就是出现的第一个1即l就是我们想要的答案。

#include<bits/stdc++.h>
using namespace std;
#define maxn 500010
const double eps=1e-4;//相当于定义一个精度常量0.0001 

double a,b,c,d,l,r,mid;

double f(double x){//原方程 
    return x*x*x*a+x*x*b+x*c+d;
}

int main(){
    cin>>a>>b>>c>>d;
    for(int i=-100;i<=100;++i){//枚举根所在的区间 
        l=i;//左区间l-mid 
        r=l+1;//右区间mid-r 
        if(f(l)==0){//找到零点 
            printf("%.2f ",l);
            continue;
        }
        if(f(l)*f(r)<0){//满足二分条件 
            while(r-l>eps){//卡精度用的 
                mid=(l+r)/2;
                if(f(mid)*f(r)<=0) l=mid;//如果mid-r满足二分条件,说明答案在此区间,将左区间更新至mid继续查询 
                else r=mid;//如果l-mid满足条件,就将右区间更新至mid 
            }
            printf("%.2f ",l);//在这个0011模型中第一个出现的1即为所求 
        }
    }
    return 0;
}

课堂例题(2) luogu 1182 数列分段 Section II

题目要求分段,求每段和的最大值最小

样例解释:

关于最大值最小:

例如一数列4 2 4 5 1要分成3

将其如下分段:

[4 2][4 5][1][42][45][1]

第一段和为6,第2段和为9,第3段和为1,和最大值为9

将其如下分段:

[4][2 4][5 1]

第一段和为4,第2段和为6,第3段和为6,和最大值为6

并且无论如何分段,最大值不会小于6。

所以可以得到要将数列4 2 4 5 1要分成3段,每段和的最大值最小为6。

我们可以把每段当做一个容器,每段和作为容器容量,每放一个数容量就会少,当容量不足以放下这个数时,我们就要拿一个新的容器

容器容量有一个标准,当某一段的和成为最小的标准时,我们就找到了答案

代码如下:

#include<bits/stdc++.h>
using namespace std;
#define maxn 500010

int now,mid,l,r,n,m,sum,v; 
int a[maxn];

bool jug(){//判断操作是否合法 
    now=mid,sum=1;//我们定义一个now作为容量,sum用来计数 
    for(int i=1;i<=n;++i){
        if(now<a[i]){//当容量不够存放下一个数字时 
            sum++;//就再分一段 
            now=mid-a[i];//更新容量上限 
        }
        else now-=a[i];//更新剩余容量 
    } 
    return sum<=m;//在sum大小合法时返回值为真 
} 

int main(){
    cin>>n>>m;
    for(int i=1;i<=n;++i){
        cin>>a[i];
        l=max(l,a[i]);//更新左区间 
        r+=a[i];//向后推进右区间 
    }
    while(l<r){//保证二分存在 
        mid=(l+r)>>1;
        if(jud()) r=mid;//若返回值为真更新右区间 
        else l=mid+1;//反之更新左区间 
    }
    cout<<l<<endl;
    return 0;
}

先坑着。。后期持续更新

猜你喜欢

转载自www.cnblogs.com/Starlight233/p/11284742.html