三分法解决凸(凹)函数极值问题

二分法只适用与线性函数,当函数脱离线性而呈现凸性或者凹性的时候,三分是很有必要的。

三分过程如下图:

凸函数:

凹函数:

实现方法:

double Calc(double p) {
   /*...*/
}


double Solve(double MIN, double MAX) {
    double Left, Right;
    double mid, midmid;
    double mid_area = 0, midmid_area = 0;    //***
    Left = MIN; Right = MAX;
    while (Left + eps < Right) {
        mid = (Left + Right) / 2;
        midmid = (mid + Right) / 2;
        mid_area = Calc(mid);
        midmid_area = Calc(midmid);

        if (midmid_area - mid_area > eps) Right = midmid;
        else Left = mid;
    }
    return mid_area;
}

 

 

  

 例题:HDU 4355 ( Party All the Time ) 

 

View Code
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#include <algorithm>
#include <string>
#include <set>
#include <ctime>
#include <queue>
#include <map>
#include <sstream>

#define CL(arr, val)    memset(arr, (val), sizeof(arr))
#define REP(i, n)       for((i) = 0; (i) < (n); ++(i))
#define FOR(i, l, h)    for((i) = (l); (i) <= (h); ++(i))
#define FORD(i, h, l)   for((i) = (h); (i) >= (l); --(i))
#define L(x)    (x) << 1
#define R(x)    (x) << 1 | 1
#define MID(l, r)   ((l) + (r)) >> 1
#define Min(x, y)   (x) < (y) ? (x) : (y)
#define Max(x, y)   (x) < (y) ? (y) : (x)
#define E(x)    (1 << (x))
#define iabs(x)  ((x) > 0 ? (x) : -(x))

typedef long long LL;
const double eps = 1e-6;
const double inf = 1000000000;

using namespace std;

const int N = 50010;

struct node {
    double p;
    double w;
} q[N];

int n;

double Calc(double p) {
    double tmp = 0, d;
    for(int i = 0; i < n; ++i) {
        d = abs(q[i].p - p);
        tmp += d*d*d*q[i].w;
    }
    return tmp;
}


double Solve(double MIN, double MAX) {
    double Left, Right;
    double mid, midmid;
    double mid_area = 0, midmid_area = 0;
    Left = MIN; Right = MAX;
    while (Left + eps < Right) {
        mid = (Left + Right) / 2;
        midmid = (mid + Right) / 2;
        mid_area = Calc(mid);
        midmid_area = Calc(midmid);

        if (midmid_area - mid_area > eps) Right = midmid;
        else Left = mid;
    }
    //printf("%.10f\n", mid_area);
    return mid_area;
}

int main() {
    //freopen("data.in", "r", stdin);

    int t, j, cas = 0;
    double mx, mi;
    scanf("%d", &t);
    while(t--) {
        scanf("%d", &n);

        mx = -inf, mi = inf;
        for(j = 0; j < n; ++j) {
            scanf("%lf%lf", &q[j].p, &q[j].w);
            if(mx < q[j].p) mx = q[j].p;
            if(mi > q[j].p) mi = q[j].p;
        }
        double ans = Solve(mi, mx) + 0.5;
        printf("Case #%d: %d\n", ++cas, int(ans));
    }
    return 0;
}

 

 POJ 3301

方法,对坐标系进行(0, 180]度的旋转,然后每个点得到新的坐标,找到最上面,最下面,最左面和最右面的点,然后就行确定当前旋转角度的面积。

x' = x*cos(th) + y*sin(th);

y' = y*cos(th) - x*sin(th);

View Code
//#pragma comment(linker,"/STACK:327680000,327680000")
#include <iostream>
#include <cstdio>
#include <cmath>
#include <vector>
#include <cstring>
#include <algorithm>
#include <string>
#include <set>
#include <functional>
#include <numeric>
#include <sstream>
#include <stack>
#include <map>
#include <queue>

#define CL(arr, val)    memset(arr, val, sizeof(arr))
#define REP(i, n)       for((i) = 0; (i) < (n); ++(i))
#define FOR(i, l, h)    for((i) = (l); (i) <= (h); ++(i))
#define FORD(i, h, l)   for((i) = (h); (i) >= (l); --(i))
#define L(x)    (x) << 1
#define R(x)    (x) << 1 | 1
#define MID(l, r)   (l + r) >> 1
#define Min(x, y)   (x) < (y) ? (x) : (y)
#define Max(x, y)   (x) < (y) ? (y) : (x)
#define E(x)        (1 << (x))
#define iabs(x)     (x) < 0 ? -(x) : (x)
#define OUT(x)  printf("%I64d\n", x)
#define Read()  freopen("data.in", "r", stdin)
#define Write() freopen("data.out", "w", stdout);

typedef long long LL;
const double eps = 1e-8;
const double pi = acos(-1.0);
const double inf = ~0u>>2;


using namespace std;

const int N = 50;

struct node {
    double x, y;
}p[N];

int n;

double Calc(double th) {
    double l = inf, r = -inf, d = inf, u = -inf;
    double xx, yy, t;
    for(int i = 0; i < n; ++i) {
        t = th*pi/180.0;
        xx = p[i].x*cos(t) + p[i].y*sin(t);
        yy = p[i].y*cos(t) - p[i].x*sin(t);
        l = min(l, xx); d = min(d, yy);
        r = max(r, xx); u = max(u, yy);
    }
    return max((r - l)*(r - l), (u - d)*(u - d));
}

double Solve(double MIN, double MAX) {
    double Left, Right;
    double mid, midmid;
    double mid_area = 0, midmid_area = 0;

    Left = MIN, Right = MAX;

    while(Left + eps < Right) {
        mid = (Left + Right) / 2.0;
        midmid = (mid + Right) / 2.0;

        mid_area = Calc(mid);
        midmid_area = Calc(midmid);
        if(midmid_area - mid_area > eps)    Right = midmid;
        else    Left = mid;
    }
    return mid_area;
}

int main() {
    //Read();
    int T, i;
    scanf("%d", &T);
    while(T--) {
        scanf("%d", &n);
        for(i = 0; i < n; ++i)  scanf("%lf%lf", &p[i].x, &p[i].y);
        printf("%.2f\n", Solve(0, 180));
    }
    return 0;
}

 

 

  

  

 

posted @ 2012-10-30 11:23  AC_Von  阅读(1473)  评论(0编辑  收藏  举报