zoukankan      html  css  js  c++  java
  • 初涉三分法

    三分法:大概是一种思想?

    何为三分

    众所周知,二分可以解决线性函数的极值问题。

    那么相应的,三分就是解决凸形/凹形函数的极值问题。

    这里有一篇博客讲的不错:二分答案法、三分法

    (图自hihocoder)

    主要的思路就是选定l,r端点,使 lmid = l+(r-l)/3 ; rmid = r-(r-l)/3 ,继而比较f(lmid)和f(rmid)

    显而易见的,若在凹形函数中,f(lmid) < f(rmid),那么$[rmid,r]$这一段一定大于最小值,故$r=rmid$;反之亦然。

    至于lmid和rmid为$[l,r]$三等分点的原因,是为了尽量使选取的两点相差较大。

    不过群里有人表示,如果函数值比较难求,可以使用黄金分割比例优化lmid和rmid。因为这样子可以重复利用函数值。

    三分板子

    luoguP3382 【模板】三分法

    题目描述

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

    输入输出格式

    输入格式:

    第一行一次包含一个正整数N和两个实数l、r,含义如题目描述所示。

    第二行包含N+1个实数,从高到低依次表示该N次函数各项的系数。

    输出格式:

    输出为一行,包含一个实数,即为x的值。四舍五入保留5位小数。


     这个没有什么好说的,直接三分。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 const double eps = 0.000001;
     4 int n;
     5 double l,r,dx[15];
     6 double val(double x)
     7 {
     8     double ret = dx[1];
     9     for (int i=2; i<=n+1; i++)
    10         ret = (ret*x)+dx[i];
    11     return ret;
    12 }
    13 void three_divide(double l, double r)
    14 {
    15     while (l+eps < r)
    16     {
    17         double lmid = l+(r-l)/3;
    18         double rmid = r-(r-l)/3;
    19         if (val(lmid) > val(rmid))
    20             r = rmid;
    21         else l = lmid;
    22     }
    23     printf("%.5f
    ",l);
    24 }
    25 int main()
    26 {
    27     scanf("%d%lf%lf",&n,&l,&r);
    28     for (int i=1; i<=n+1; i++)
    29         scanf("%lf",&dx[i]);
    30     three_divide(l, r);
    31     return 0;
    32 }
    lg3382

    HDU2899Strange fuction

    Problem Description
    Now, here is a fuction:
      F(x) = 6 * x^7+8*x^6+7*x^3+5*x^2-y*x (0 <= x <=100)
    Can you find the minimum value when x is between 0 and 100.
    > 现在有一个函数F(x) = 6 * x^7+8*x^6+7*x^3+5*x^2-y*x (0 <= x <=100).找到它在[0,100]内的最小值。
    Input
    The first line of the input contains an integer T(1<=T<=100) which means the number of test cases. Then T lines follow, each line has only one real numbers Y.(0 < Y <1e10)
    Output
    Just the minimum value (accurate up to 4 decimal places),when x is between 0 and 100.
    > 四舍五入到4位小数

     这个没有讲明是三分,不过我们可以先观察观察,发现F'(x)在[0,+∞)上是一个增函数。

    法一:对于F'(x)二分求零点

     1 #include<cstdio>
     2 #include<cmath>
     3 const double eps = 1e-6;
     4 double y,l,r,mid;
     5 int tt;
     6 double dx(double x)
     7 {
     8     return 42*pow(x,6)+48*pow(x,5)+21*pow(x,2)+10*x-y;
     9 }
    10 double f(double x)
    11 {
    12     return 6*pow(x,7)+8*pow(x,6)+7*pow(x,3)+5*x*x-y*x;
    13 }
    14 int main()
    15 {
    16     scanf("%d",&tt);
    17     for (; tt; tt--)
    18     {
    19         scanf("%lf",&y);
    20         l = 0;r = 100;
    21         while (l+eps < r)
    22         {
    23             mid = (l+r)/2;
    24             if (dx(mid) > 0)
    25                 r = mid;
    26             else l = mid;
    27         }
    28         printf("%.4f
    ",f(l));
    29     }
    30     return 0;
    31 }

    法二:发现它在[0,100]上是一个凹形函数,故三分

     1 #include<cstdio>
     2 const double eps = 0.000001;
     3 int tt;
     4 double y;
     5 double f(double x)
     6 {
     7     double ret = 6*x*x*x*x*x*x*x+8*x*x*x*x*x*x+7*x*x*x+5*x*x-y*x;
     8     return ret;
     9 }
    10 void three_divide(double l, double r)
    11 {
    12     while (l+eps < r)
    13     {
    14         double lmid = l+(r-l)/3;
    15         double rmid = r-(r-l)/3;
    16         if (f(lmid) < f(rmid))
    17             r = rmid;
    18         else l = lmid;
    19     }
    20     printf("%.4lf
    ",f(l));
    21 }
    22 int main()
    23 {
    24     scanf("%d",&tt);
    25     for (; tt; tt--)
    26     {
    27         scanf("%lf",&y);
    28         three_divide(0, 100);
    29     }
    30     return 0;
    31 }

    hiho1142 : 三分·三分求极值

    描述

    这一次我们就简单一点了,题目在此:

    在直角坐标系中有一条抛物线y=ax^2+bx+c和一个点P(x,y),求点P到抛物线的最短距离d。

    输入

    第1行:5个整数a,b,c,x,y。前三个数构成抛物线的参数,后两个数x,y表示P点坐标。-200≤a,b,c,x,y≤200

    输出

    第1行:1个实数d,保留3位小数(四舍五入)


    这道题就比较有趣了。

    容易想到定义函数为P点到抛物线上横坐标为$x_0$的点的距离。那么我们来观察一下这个函数:

    由于$a$,$b$,$c$,$x$,$y$都是给定的,这个看上去很复杂的东西就是的形式(并且根号下的多项式必定为非负数)。那么就很显然,$f(x)$可以三分查找最值。

    接下去的问题就变成了怎么选取三分的$l$,$r$:

    一种无脑方法就是直接一个-INF和一个INF拿来做。

    另一种方法是分类讨论:若P点被抛物线围住(同向抛物线开口方向),就在y=a(x)的两根之中寻找(a(x)表示抛物线函数);若P点在抛物线外,就是对称轴和x之间寻找。

     1 #include<bits/stdc++.h>
     2 const double eps = 0.000001;
     3 int a,b,c,d,x,y;
     4 double calc(double x)
     5 {
     6     return a*x*x+b*x+c;
     7 }
     8 double f(double xx)
     9 {
    10     return sqrt(pow(calc(xx)-y, 2)+(x-xx)*(x-xx));
    11 }
    12 void three_divide(double l, double r)
    13 {
    14     while (r-l > eps)
    15     {
    16         double lmid = l+(r-l)/3;
    17         double rmid = r-(r-l)/3;
    18         if (f(lmid) < f(rmid))
    19             r = rmid;
    20         else l = lmid;
    21     }
    22     printf("%.3f
    ",f(l));
    23 }
    24 int main()
    25 {
    26     scanf("%d%d%d%d%d",&a,&b,&c,&x,&y);
    27     double pl = -b*1.0/(2*a*1.0);
    28     double ll = std::min(pl, x*1.0);
    29     double rr = std::max(pl, x*1.0);
    30     double del = b*b*1.0+4*a*y-4*a*c;
    31     if (del >= 0 && ((a > 0 && calc(x) <= y) || (a < 0 && calc(x) >= y)))
    32     {
    33         ll = (-b-sqrt(del))/(2*a*1.0);
    34         rr = (-b+sqrt(del))/(2*a*1.0);
    35     }
    36     three_divide(ll, rr);
    37     return 0;
    38 }

    END

  • 相关阅读:
    flask
    admin站点管理
    DRF
    基础
    通过djangocrontab扩展来实现 定时任务
    配置asgi来达到能处理websocket
    用es实现模糊搜索
    迁移问题
    启动活动的最佳写法
    启动活动的最佳写法
  • 原文地址:https://www.cnblogs.com/antiquality/p/8795973.html
Copyright © 2011-2022 走看看