zoukankan      html  css  js  c++  java
  • 二分、三分小记

    前言

    移植于原csdn博客
    对于这两种算法,往往会配上其它算法,它们通常会转换成判定性问题,往往需要控制它们的精度


    一些模板

    二分(最大值最小)

    整数域

    while (l<r){
    	rr int mid=(l+r)>>1;
    	if (check(mid)) r=mid;
    		else l=mid+1;
    }
    return l;
    

    实数域

    const double eps=1e-8;
    while (l+eps<r){
    	rr int mid=(l+r)/2;
    	if (check(mid)) r=mid;
    		else l=mid;
    }
    return l;
    

    二分(最小值最大)

    整数域

    while (l<r){
    	rr int mid=(l+r+1)>>1;
    	if (check(mid)) l=mid;
    		else r=mid-1;
    }
    return l;
    

    实数域

    const double eps=1e-8;
    while (l+eps<r){
    	rr int mid=(l+r)/2;
    	if (check(mid)) l=mid;
    		else r=mid;
    }
    return l;
    

    三分(单峰函数)

    实数域

    const double eps=1e-8;
    while (l+eps<r){
        rr double o=(r-l)/3,lmid=l+o,rmid=r-o;
        if (calc(lmid)>calc(rmid)) r=rmid;
    	    else l=lmid;
    }
    return l;
    

    整数域

    rr int now=l;
    while (l+2<r){
        rr int o=(r-l)/3,lmid=l+o,rmid=r-o;
        if (calc(lmid)>calc(rmid)) r=rmid;
    	    else l=lmid;
    }
    for (rr int i=l;i<=r;++i)
        if (calc(i)>calc(now)) now=i;
    return now;
    

    三分(单谷函数)

    实数域

    const double eps=1e-8;
    while (l+eps<r){
        rr double o=(r-l)/3,lmid=l+o,rmid=r-o;
        if (calc(lmid)<calc(rmid)) r=rmid;
    	    else l=lmid;
    }
    return l;
    

    整数域

    rr int now=l;
    while (l+2<r){
        rr int o=(r-l)/3,lmid=l+o,rmid=r-o;
        if (calc(lmid)<calc(rmid)) r=rmid;
    	    else l=lmid;
    }
    for (rr int i=l;i<=r;++i)
        if (calc(i)<calc(now)) now=i;
    return now;
    

    二分

    HDU 2199 Can you solve this equation

    题目

    (y=8x^4+7x^3+2x^2+3x+6)(x)([0sim 100])的解


    分析

    首先(y)是单调上升的,所以需要用到二分,但是首先要判断是否无解,精度是真的恶心


    代码

    #include <cstdio>
    #define rr register
    using namespace std;
    int n;
    signed main(){
        scanf("%d",&n);
        for (rr int i=1;i<=n;++i){
            rr double y,l=0,r=100; scanf("%lf",&y);
            if (y<6||y>807020306) printf("No solution!
    ");
            else{
                while (l+1e-8<r){
                    rr double mid=(l+r)/2;
                    rr double f=mid*mid*mid*mid*8+7*mid*mid*mid+2*mid*mid+3*mid+6;
                    if (f>y) r=mid-1e-6;
                        else if (f<y) l=mid+1e-6;
                            else l=r=mid;
                }
                printf("%.4lf
    ",l);
            }
        }
        return 0;
    }
    

    HDU 1551 Cable master

    题目

    (n)块木板切成相同的(k)块,不可以拼接,问这(k)块的最大长度


    分析

    二分答案,判定条件就是是否能切成相同的(k)块,感觉这种题目做过很多次了


    代码

    #include <cstdio>
    #define rr register
    using namespace std;
    int n,k; double a[10001];
    signed main(){
        while (scanf("%d%d",&n,&k)==2&&n&&k){
            rr double l=0,r=0;
            for (rr int i=1;i<=n;++i) scanf("%lf",&a[i]),r=r>a[i]?r:a[i];
            while (l+1e-4<r){
                rr double mid=(l+r)/2; rr int sum=0;
                for (rr int i=1;i<=n;++i) sum+=a[i]/mid;
                if (sum>=k) l=mid;
                    else r=mid;
            }
            printf("%.2lf
    ",l);
        }
        return 0;
    }
    

    三分

    洛谷 3382 【模板】三分法

    题目

    给出一个(N)次函数,保证在范围([l,r])内存在一点(x),使得([l,x])上单调增,([x,r])上单调减。试求出(x)的值。


    分析

    首先如何快速求出(N)次函数的值呢,可以用秦九韶算法,既然是三分,那么就是这样的

    (伪代码)
    while (l+eps<r){
            k=(r-l)/3.0;
            if (answ(l+k)>answ(r-k)) r-=k;
                else l+=k;
    }
    

    然而如果要满足三分,必须严格单峰或者单谷,否则三分不再适用


    代码

    #include <cstdio>
    #define rr register
    using namespace std;
    typedef double db;
    const db eps=1e-7;
    db a[15],l,r; int n;
    inline db answ(db x){
        rr db sum=0;
        for (rr int i=0;i<=n;++i) sum=sum*x+a[i];
        return sum;
    }
    signed main(){
        scanf("%d%lf%lf",&n,&l,&r);
        for (rr int i=0;i<=n;++i) scanf("%lf",&a[i]);
        while (l+eps<r){
            rr db k=(r-l)/3.0;
            if (answ(l+k)>answ(r-k)) r-=k;
                else l+=k;
        }
        return !printf("%.5lf",l);
    } 
    

    HDU 2899 Strange fuction

    题目

    (y=6x^7+8x^6+7x^3+5x^2-ax),给定(a),问(x)([0sim 100])中令(y)最小的值


    分析

    可以发现这应该是一个单谷函数,因为若没有(-ax),就直接单调上升,但是一旦加了上去,感性理解,中间会有一个最小值,所以可以用三分解决


    代码

    #include <cstdio>
    #define rr register
    using namespace std;
    int n; double y;
    inline double f(double x){
        return x*x*x*x*x*x*x*6+x*x*x*x*x*x*8+x*x*x*7+x*x*5-y*x;
    }
    signed main(){
        scanf("%d",&n);
        for (rr int i=1;i<=n;++i){
            rr double l=0,r=100;
            scanf("%lf",&y);
            while (l+1e-8<r){
                rr double k=(r-l)/3.0;
                if (f(l+k)<f(r-k)) r-=k;
                    else l+=k; 
            }
            printf("%.4lf
    ",f(l));
        }
        return 0;
    }
    

    洛谷 2600 JZOJ 1721 瞭望塔

    题目

    用一条山的上方轮廓折线((x1, y1), (x2, y2),dots,(xn, yn))来描述H村的形状,这里(x1 < x2 < …< xn)。瞭望塔可以建造在([x1, xn])间的任意位置, 但必须满足从瞭望塔的顶端可以看到H村的任意位置。可见在不同的位置建造瞭望塔,所需要建造的高度是不同的。为了节省开支,希望建造的塔高度尽可能小。


    分析

    对于每两个点之间三分横坐标,因为两个横坐标之间的所需高度应该是单谷的,为了看到右边,左边会高,为了看到左边,右边会高。等到找到一个中间点的时候,判断最大的还需要的高度,这样求出函数值,最后对于每两个点之间求一次最小值。


    代码

    #include <cstdio>
    #include <cctype>
    #define rr register
    using namespace std;
    typedef double db;
    const db eps=1e-9;
    int n,x[301],y[301];
    db k[301],b[301],ans=1e20;
    inline signed iut(){
        rr int ans=0,f=1; rr char c=getchar();
        while (!isdigit(c)) f=(c=='-')?-f:f,c=getchar();
        while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
        return ans*f;
    }
    inline void min(db &a,db b){if (a>b) a=b;}
    inline void max(db &a,db b){if (a<b) a=b;}
    inline db answ(db x0,db y0){
        rr db ans=0;
        for (rr int i=1;i<n;++i)
            max(ans,k[i]*x0+b[i]-y0);
        return ans;
    }
    signed main(){
        n=iut();
        for (rr int i=1;i<=n;++i) x[i]=iut();
        for (rr int i=1;i<=n;++i) y[i]=iut();
        for (rr int i=1;i<n;++i)
            k[i]=(y[i]-y[i+1])*1.0/(x[i]-x[i+1]),
            b[i]=y[i]-k[i]*x[i];
        for (rr int i=1;i<n;++i){
            rr db l=x[i],r=x[i+1];
            while (l+eps<r){
                rr db t=(r-l)/3.0,lmid=l+t,rmid=r-t;
                if (answ(lmid,k[i]*lmid+b[i])<answ(rmid,k[i]*rmid+b[i])) r=rmid;
                    else l=lmid;
            }
            min(ans,answ(l,k[i]*l+b[i]));
        }
        return !printf("%.3lf",ans);
    } 
    
  • 相关阅读:
    SSIS 错误代码 DTS_E_OLEDB_EXCEL_NOT_SUPPORTED 没有可用的 OLE DB 访问接口 SSIS 的 64 位版本中不支持 Excel 连接管理器
    WIN8安装SQL SERVER2008:出现创建userSettings/Microsoft.SqlServer.Configuration.LandingPage.Properties.Settings 的配置节处理程序时出错的解决办法。
    WIN8安装SQL SERVER2008:出现"MsiGetPRoductInfo” 无法检索 Product Code {47439CBBCF57485B9994BE9E02D29193}的包的 ProductVersion;解决办法。
    解读QT信号与槽机制里 QMetaObject::connectSlotsByName(QObject *o)的源码
    初涉Windows Mobile下的QT程序移植和部署
    C++虚函数探索笔记(1)——虚函数的简单示例分析
    一个不应该发生的BUG。。。
    Qt.Visual.Studio.Integration.1.4.3 与 QT 4.5.0 Commercial for VS2008搭档的一个小问题
    C++ 逗号操作符二三事
    C++虚函数探索笔记(3)——延伸思考:虚函数应用的一些其他情形
  • 原文地址:https://www.cnblogs.com/Spare-No-Effort/p/13854599.html
Copyright © 2011-2022 走看看