二分/三分

这篇博客详细介绍了如何使用二分和三分搜索算法来求解单调函数的方程解和最值问题。通过实例代码展示了在连续和不连续数据中的应用,并讨论了在精度要求较高的情况下如何调整算法。此外,还分享了一个在解决实际问题时对二分法错误分析的过程,强调了正确理解函数单调性的重要性。
摘要由CSDN通过智能技术生成

二分学习 +板子

参考资料

一、查找 等于某个值的 位置(下标)/ 对应的变量

在不连续的数据中查找 即数组

int Bifi(int a[],int n,int x){
	int l=0,r=n-1;
	while(l<=r){
		int mid=(l+r)/2;
		if(x==a[mid])return mid;
		if(x>a[mid])l=mid+1;
		else r=mid-1;
	}
	retrn -1;
}

在连续的数据中查找 (仅限单调函数

xx f(xx ?){return 函数值;}
xx Bifi(xx l,xx r,xx y){
	while(r-l> 精确最小误差 ){
		int mid=(l+r)/2;
		if(f(mid)>y)r=mid-精确最小误差;
		else l=mid+精确最小误差;
	}
	retrn (l+r)/2;
}

二、查找 第一个大于等于某个值的 位置(下标)

可以用:lower_bound()代替参考资料

int Bifi(int l,int r,int x){
	int pos;
	while(l<=r){
		int mid=l+r>>1;
		if(dp[mid]>=x){
			pos=mid;
			r=mid-1;
		}
		else l=mid+1;
	}
	return pos;
}

若是查找 第一个大于某个值的位置

可以用:upper_bound()代替参考资料
将上面代码中的 if(dp[mid]>=x)改成if(dp[mid]>x)即:

int Bifi(int l,int r,int x){
	int pos;
	while(l<=r){
		int mid=l+r>>1;
		if(dp[mid]>x){
			pos=mid;
			r=mid-1;
		}
		else l=mid+1;
	}
	return pos;
}

三分学习

两个端点间取三等分,中间两个点哪一个的函数值更靠近最值,则取两个点中的另一个点为端点,
再循环上述操作
直至中间两个点无限逼近(或者说其差值小于误差值),则极值就为两点的中间的函数值

水题集

二分:

hdu 2199:找单调函数方程解 ->连续的二分
#include <bits/stdc++.h>
using namespace std;

//double f(double x){return 8*x*x*x*x +7*x*x*x +2*x*x+3*x+6; }
double f(double x){return 8*pow(x,4.0)+7*pow(x,3.0)+2*pow(x,2.0)+3*x+6; }
double Bifi(double y,double l,double r){
    while(r-l>1e-7){
        double mid=(r+l)/2;
        if(f(mid)>y )r=mid-1e-7;
        else l=mid+1e-7;
    }
    return (l+r)/2;
}

int main(){
    //ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int t;scanf("%d",&t);
    while(t--){
        double y;scanf("%lf",&y);
        if(y<6 || y>f(100)){printf("No solution!\n");continue;}
        printf("%.4f\n",Bifi(y,0.0,100.0));
    }
    return 0;
}

三分:

hdu 2899:求凸性函数最值:一个函数三分

HDU 2899

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

double f(double x,double y){return 6*pow(x,7.0)+8*pow(x,6.0)+7*pow(x,3.0)+5*pow(x,2.0)-y*x; }

double Trfi(double y,double l,double r){
    double a=(l*2+r)/3,b=(r*2+l)/3,ans=0.0;
    while(b-a>1e-7){
        if(f(b,y)>f(a,y))r=b;
        else l=a;
        a=(l*2+r)/3;b=(r*2+l)/3;
    }
    return f( (a+b)/2 ,y );
}

int main(){
    //ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int t;scanf("%d",&t);
    while(t--){
        double y;scanf("%lf",&y);
        printf("%.4f\n",Trfi(y,0.0,100.0));
    }
    return 0;
}
hdu 3714:求多个函数构成的图形函数的最值

HDU 3714

分析:求F(x)=max(Si(x));
画图分析易知F(x)一定是一个下凸的图像,三分
这个题的精度要注意,还不会精度到底要怎么搞,我一般都取1e-7,这次wa了,取了1e-10过了。

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

//把F[x]=max(s(x))表示出来;
int n;

struct node{double a,b,c;}p[10010];
double S(double a,double b,double c,double x){return a*x*x+b*x+c;}
double F(double x){
    double m=S(p[1].a,p[1].b,p[1].c,x);
    for(int i=2;i<=n;i++){
        m=max(m, S(p[i].a,p[i].b,p[i].c,x) );
    }
    return m;
}

double Trfi(double l,double r){
    double ll=(l*2+r)/3,rr=(r*2+l)/3;
    while(rr-ll>1e-10){
        if( F(ll)>F(rr) ) l=ll;
        else r=rr;
        ll=(l*2+r)/3,rr=(r*2+l)/3;
    }
    return F((rr+ll)/2);
}

int main(){
    int t;scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%lf%lf%lf",&p[i].a,&p[i].b,&p[i].c);
        }
        printf("%.4f\n",Trfi(0.0,1000.0));
    }
    return 0;
}

这个题又学到了一个“代码技巧”:就是多个函数取最大值(最小值)的操作

wa多次 ?水题集?

二分:

poj:1905

POJ 1905-二分

做这个题做到怀疑智商了QAQ

分析:
由题意已知:L ,s ,0<h<L/2 ; 求 h;
在这里插入图片描述

在这里插入图片描述

那么知道S(R(h))的单调性就可以知道用二分还是三分求解了;

下面是错误分析(这段分析真的还以自己还有没有智商了)
根据 h 的范围;我对①式中的 h 求导;得出 r 随 h 的增大而减小(即单调减函数);然后我一看②式妥妥的单调增函数啊;然后我就判定这个复合函数为单调减函数了。
但是我突然发现我运行样例的时候,???,怎么都不对,我就去翻别人的题解,从别人的代码里我发现一个问题,就是这个复合函数是单调增的,然后我质疑了自己的思路,没问题啊,又求了很多次导;终于发现woc,那个式子②他不是单增啊,然后又发现,我不会求这个函数;

所以正确的分析应该是这样的:
h 增加,那个弧长一定是增加的啊,也就是我那个复合函数是单增的。woc我在想什么????想求导想疯了;

但翻别人写的题解的时候,我还是有收获的:
就是二分的时候精度问题:还可以通过直接二分100次解决,但是感觉不靠谱,我三分用了100,直接TLE了,别人的二分100次;但是吧现在还没遇到卡精度卡那么高的题,等以后吃亏了再来写写这个while(100);

#include <iostream>
#include <cmath>
using namespace std;

double L,n,c;

double f(double h){
	double r=(4*h*h+L*L)/(8*h);
	return 2*r*asin(L/(2*r));
}

double Bifi(double l,double r,double s){
	while(r-l>1e-6){
		double mid=(r+l)/2;
		if( s>f(mid) )l=mid;
		else r=mid;
	}
	return (r+l)/2;
}

int main(){
	while(scanf("%lf%lf%lf",&L,&n,&c)){
		if(L<0&&n<0&&c<0)break;
		double s=(1+n*c)*L;
		printf("%.3f\n",Bifi(0.0,L/2,s));
	}
	return 0;
}

三分:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值