三分法
1.三分法模板
double Calc(Type a) { /* 根据题目的意思计算 */ } void Solve(void) { double Left, Right; double mid, midmid; double mid_value, midmid_value; Left = MIN; Right = MAX; while (Left + EPS < Right) { mid = (Left + Right) / 2; midmid = (mid + Right) / 2; mid_value = Calc(mid); midmid_value = Calc(midmid); // 假设求解最大极值. if (mid_value >= midmid_value) Right = midmid; else Left = mid; } }
2.两重三分法
#include <math.h> #include <stdio.h> #include <iostream> using namespace std; #define INF 0x7fffffff #define eps 1e-5 int n, m, s1, s2, s3; double Calc(double x, double y) /* x, y, n-x-y分别表示三段横向距离 */ { return sqrt(pow(m,2)+pow(x,2))/s1 + sqrt(pow(m,2)+pow(y,2))/s2 + sqrt(pow(m,2)+pow(n-x-y,2))/s3; } int main() { double ans = INF; scanf("%d %d %d %d %d", &n, &m, &s1, &s2, &s3); double XL, XR, XM, XMM, Xt1, Xt2; double YL, YR, YM, YMM, Yt1, Yt2; XL = 0; XR = n; /* 外层对y取值透明,x为唯一变量时推极小值 */ while(XR - XL > eps) { XM = (XL + XR) / 2; XMM = (XM + XR) / 2; YL = 0; YR = n; /* 当x固定为XM,y为唯一变量时推极小值 */ while(YR - YL > eps) { YM = (YL + YR) / 2; YMM = (YM + YR) / 2; Yt1 = Calc(XM, YM); Yt2 = Calc(XM, YMM); if(Yt1 > Yt2) YL = YM; else YR = YMM; } Xt1 = Calc(XM, YL); // 或Xt1 = Calc(XM, YR); YL = 0; YR = n; /* 当x固定为XMM,y为唯一变量时推极小值 */ while(YR - YL > eps) { YM = (YL + YR) / 2; YMM = (YM + YR) / 2; Yt1 = Calc(XMM, YM); Yt2 = Calc(XMM, YMM); if(Yt1 > Yt2) YL = YM; else YR = YMM; } Xt2 = Calc(XMM, YL); // 或Xt2 = Calc(XMM, YR); if(Xt1 > Xt2) { XL = XM; ans = Xt2; } else { XR = XMM; ans = Xt1; } } printf("%.10f\n", ans); return 0; }
思路分析:
从Calc公式不难看出函数图像在x-y-z三维坐标中是一个下凸曲面,现在题目要求转换为:找到曲面最低处(x', y'),使得z = Calc(x', y')达到最小,解决这类三维图像(二元)求极值问题就是转化成二维图像(一元)求极值问题,就可以用普通三分法求解,这里采用两重三分法,外层用来逐步逼近目标x',内层用来逐步逼近目标y':
当x固定在某值时,用普通三分法再确定y求得当前最小值是很容易做到的……在这一系列最小值的集合再选最小值事实上又是一个二维图像(一元)求极值问题,这可以从曲面的纵向剖物面看出,那么程序内外层就是依据这个思路编写的。