三分法求解凸函数的最值

三分法求解凸函数的最值

二分法作为分治中最常见的方法,适用于单调函数,逼近求解某点的值。但当函数是凸性函数时,二分法就无法适用,这时三分法就可以“大显身手”

函数必须严格单调递减

对于如下图的一个凸函数 $ f(x),x∈[left,right] \(其中 lm 和 rm 分别为区间[left,right]的三等分点,我们发现如果\) f(lm)<f(rm) $,那么函数值最小的点的横坐标x一定在[left,rm]之间。 同理,当f(lm)>f(rm)时,最值的横坐标x一定在[lm,right]的区间内。

利用这个性质,我们就可以在缩小区间的同时向目标点逼近,从而得到极值。

while(r-l > 0.000...1)

题目若要保留x位就小数点后x个0

也可写成 1e-(x+1)

模板

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define db double
using namespace std;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)) {x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
const db eps=1e-6;
int n;
db l,r,a[15],ans;
db qpow(db a,int b){
	db res=1;
	while(b){
		if(b&1) res*=a;
		a*=a;
		b>>=1;
	}
	return res;
}
db get(db x) {
	db res=0;
	for(int i=0;i<=n;i++)
		res+=qpow(x,i)*a[i];
	return res;
}
int main() {
	scanf("%d",&n);
	scanf("%lf%lf",&l,&r);
	for(int i=n;i;i--)
		scanf("%lf",&a[i]);
	while(r-l>eps){
		db lm=l+(r-l)/3;
		db rm=r-(r-l)/3;		
		get(lm)>get(rm)?r=rm,ans=lm:l=lm,ans=rm; 
	}
	printf("%.5lf\n",ans);
	return 0;
}

模板2

我们令pivot代表抛物线的对称抽,可以发现当X>pivot,我们可以取left = pivot,right = inf, 反之left = -inf , right = pivot, 其距离恰好满足凸形函数。而我们要求的最短距离d,正好就是这个凸形函数的极值。

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const double MAX = 100000;
double a,b,c,x,y;
double pivot;
double dis(double x_){
    double y_=a*x_*x_+b*x_+c;
    return sqrt((x-x_)*(x-x_)+(y-y_)*(y-y_));
}
double solve(double l,double r){
    double lm=l+(r-l)/3;
    double rm=r-(r-l)/3;
    double lmd=dis(lm);
    double rmd=dis(rm);
    if(fabs(lmd-rmd)<0.0001) return lmd;
    if(lmd>rmd) return solve(lm,r);
    else return solve(l,rm);
}
int main(){
    while(scanf("%lf%lf%lf%lf%lf",&a,&b,&c,&x,&y)!=EOF){
        pivot=-b/(2*a);
        double l=0,r=0;
        if(pivot<x){
            l=pivot+0.0001;
            r=MAX;
        }else{
            l=-MAX;
            r=pivot-0.0001;
        }
        printf("%.3lf\n",solve(l,r));
    }

    return 0;
}

https://blog.csdn.net/controlbear/article/details/54237388

咆咆咆哮

Solution

https://www.cnblogs.com/chenxiaoran666/p/CometOJDay4Div1I.html

fuc...拍了一上午都没拍出来。。。就是wa

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
inline int read() {
	int x=0,f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int n;
int a[100005],b[100005],s[100005];
inline void Max(long long &x,long long y){if(x<y) x=y;}
inline long long get(int x){
    long long  res=0;
    for(int i=1;i<=n;i++)
        res+=a[i],s[i]=(1ll)*b[i]*x-a[i];
    sort(s+1,s+1+n);
    reverse(s+1,s+1+n);
    for(int i=1;i<=n-x;i++) res+=s[i];
    return res;
}
inline long long solve(int l,int r){
    long long res=0;
    while(r-l>2){
        int  lm=l+(r-l)/3,rm=r-(r-l)/3;
        get(lm)>get(rm)?r=rm:l=lm;
    }
    for(int i=l;i<=r;i++)
        Max(res,get(i));
    return res;
}
int main(){
    n=read();
    for(int i=1;i<=n;i++)
        a[i]=read(),b[i]=read();
    printf("%lld\n",solve(1,n));
    return 0;
}

std

#include <bits/stdc++.h>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int &
#define I inline
#define N 100000
#define LL long long
#define Gmax(x, y) (x < (y) && (x = (y)))
using namespace std;
int n, a[N + 5], b[N + 5];
LL s[N + 5];
inline int read()
{
    int x = 0, f = 1;
    char ch = getchar();
    while (!isdigit(ch))
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (isdigit(ch))
    {
        x = x * 10 + ch - '0';
        ch = getchar();
    }
    return x * f;
}
I LL GetAns(CI x) //求出在使用a[i]的卡牌张数为x时的最优答案
{
    RI i;
    Reg LL res = 0;
    for (i = 1; i <= n; ++i)
        res += a[i], s[i] = 1LL * b[i] * x - a[i]; //初始化答案
    for (sort(s + 1, s + n + 1), i = n; i ^ x; --i)
        res += s[i];
    return res; //排序+贪心,返回答案
}
I LL Solve(RI l, RI r) //三分
{
    RI i, lm, rm;
    Reg LL res = 0;
    while (r - l > 2)
    {
        lm = l + (r - l) / 3, rm = r - (r - l) / 3;
        GetAns(lm) > GetAns(rm) ? r = rm : l = lm;
    }

    for (i = l; i <= r; ++i)
        Gmax(res, GetAns(i));
    return res;
}
int main()
{
    n = read();
    for (int i = 1; i <= n; ++i)
        a[i] = read(), b[i] = read();
    printf("%lld", Solve(1, n));
    return 0;
}
posted @ 2020-09-02 22:07  ke_xin  阅读(311)  评论(0编辑  收藏  举报