三分法解决凸(凹)函数极值问题
二分法只适用与线性函数,当函数脱离线性而呈现凸性或者凹性的时候,三分是很有必要的。
三分过程如下图:
凸函数:
凹函数:
实现方法:
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; }