三分学习(解决凸函数or凹函数的极值)

来源:互联网 发布:淘宝访客来源 网址 编辑:程序博客网 时间:2024/05/16 14:33

思想转载自:http://blog.csdn.net/pi9nc/article/details/9666627


如果遇到凸性或凹形函数时,可以用三分查找求那个凸点或凹点。


如图所示,已知左右端点L、R,要求找到白点的位置。
思路:通过不断缩小 [L,R] 的范围,无限逼近白点。
做法:先取 [L,R] 的中点 mid,再取 [mid,R] 的中点 mmid,通过比较 f(mid) 与 f(mmid) 的大小来缩小范围。
           当最后 L=R-1 时,再比较下这两个点的值,我们就找到了答案。
1、当 f(mid) > f(mmid) 的时候,我们可以断定 mmid 一定在白点的右边。
反证法:假设 mmid 在白点的左边,则 mid 也一定在白点的左边,又由 f(mid) > f(mmid) 可推出 mmid < mid,与已知矛盾,故假设不成立。
所以,此时可以将 R = mmid 来缩小范围。
2、当 f(mid) < f(mmid) 的时候,我们可以断定 mid 一定在白点的左边。
反证法:假设 mid 在白点的右边,则 mmid 也一定在白点的右边,又由 f(mid) < f(mmid) 可推出 mid > mmid,与已知矛盾,故假设不成立。


同理: 当求凹点的时候,当f(m) >  f(mm)的时候 ,L =m;
                                             当f(m) < f(mm) 的时候,R = mm;证明与上面一般;


zoj3203:


题意:

人左右走动,求影子L的最长长度。

思路:求最长的影子长度;

设x为人灯光垂直线的距离,那么有两个边界,一个是当影子刚好射到墙角的时候,即x = h / H * D ,一个是全都投射到墙上的情况即x = D;

这个肯定在中间的某个位置最长,利用calc(x)公式求出最长的长度,calc公式推导如下:



可以得出L=  H - D * (H - h)/x +D -x,不过得注意x的起点是从h / H * D 开始的;

#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#include<vector>#include<algorithm>using namespace std;const int maxn = 100000 + 10;#define INF 0x7fffffff#define clr(x,y) memset(x,y,sizeof(x))typedef long long ll;#define eps 10e-10double H,h,D;double calc(double x){    return H - D * (H - h)/x +D -x;}int main(){    int Tcase;    scanf("%d",&Tcase);    while(Tcase --)    {        scanf("%lf%lf%lf",&H,&h,&D);        double l = D - h /H * D,r = D;        while(l + eps < r)        {            double m = (l + r) / 2, mm = (m + r) /2;            if(calc(m) < calc(mm))            {                l = m;            }            else            {                r = mm;            }        }        printf("%.3lf\n",calc(l));    }    return 0;}

当然还有数学方法可以解决这个题:

公式为:L = H + D - ((H - h)*D /x + x),你会发现这就是一个双勾函数,只要把这个函数的最小值,L的最大值就能求出来了,双勾函数的性质,对于一个双勾函数: a*x + b / x .(a> 0 && b > 0)



然后判断一下边界取出最小值代入得到最大值。

#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#include<vector>#include<algorithm>using namespace std;const int maxn = 100000 + 10;#define INF 0x7fffffff#define clr(x,y) memset(x,y,sizeof(x))typedef long long ll;#define eps 10e-10double H,h,D;double calc(double x){    return H + D - ((H - h)*D /x + x);}int main(){    int Tcase;    scanf("%d",&Tcase);    while(Tcase --)    {        scanf("%lf%lf%lf",&H,&h,&D);        double xm = sqrt(D*(H - h));        double l = D - h / H * D,r = D;        double ans;        if(l <= xm && r >= xm)            ans = calc(xm);        else if(xm < l)            ans = calc(l);        else ans = calc(r);        printf("%.3lf\n",ans);    }    return 0;}

HDU 2438

题意:已知汽车的长和宽,l和w,以及俩条路的宽为x和y,汽车所处道路宽为x ,问汽车能否顺利转弯?


分析:汽车能否顺利转弯取决于在极限情况下,随着角度的变化,汽车离对面路的距离是否大于等于0

在上图中需要计算转弯过程中h 的最大值是否小于等于y


很明显,随着角度θ的增大,最大高度h先增长后减小,即为凸性函数,可以用三分法来求解


如图中:


这题转载自http://www.cnblogs.com/nanke/archive/2012/02/12/2348041.html


#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#include<vector>#include<algorithm>using namespace std;const int maxn = 1000000 + 10;#define INF 0x7fffffff#define clr(x,y) memset(x,y,sizeof(x))typedef long long ll;#define eps 10e-10double x,y,l,w;double calc_h(double angle){    double s = l * cos(angle) + w *sin(angle) - x;    return s * tan(angle) + w * cos(angle);}int main(){    while( ~ scanf("%lf%lf%lf%lf",&x,&y,&l,&w))    {        double L = 0,R = acos(-1.0)/2;        while(L + eps < R)        {            double m = (L + R) / 2,mm = (m + R)/2;            if(calc_h(m) < calc_h(mm))            {                L = m;            }            else R = mm;        }        double max_h = calc_h(L);        if(max_h <= y)        {            cout << "yes" << endl;        }        else cout << "no" << endl;    }    return 0;}




0 0
原创粉丝点击