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

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


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

三分学习(解决凸函数or凹函数的极值)_第1张图片
如图所示,已知左右端点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:

三分学习(解决凸函数or凹函数的极值)_第2张图片

题意:

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

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

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

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


三分学习(解决凸函数or凹函数的极值)_第3张图片

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

#include
#include
#include
#include
#include
#include
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-10
double 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)


三分学习(解决凸函数or凹函数的极值)_第4张图片

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

#include
#include
#include
#include
#include
#include
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-10
double 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先增长后减小,即为凸性函数,可以用三分法来求解


如图中:

三分学习(解决凸函数or凹函数的极值)_第5张图片

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


#include
#include
#include
#include
#include
#include
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-10
double 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;
}




你可能感兴趣的:(三分)