三分法

三分法可以求出单峰函数(不一定是凸函数,可以是下面这样)的最大值(峰值),或者单谷函数的最小值(谷值)。
image
我们以单峰函数为例,在 \([l,r]\)(最小值所在范围)任取 \(l \le lmid \le rmid \le r\),求出 \(f(lmid)\)\(f(rmid)\)
image
如若有 \(f(lmid) > f(rmid)\),那么 \(rmid \sim r\) 这一段区间一定是递减的,可以缩小范围到 \((l, rmid)\)
在实数域上三分,精度为 \(eps\) 的话,考虑什么时候结束三分。如果 \(l = r - 2 eps\),这时刚好会出现无限循环,也就是 \(l\) 不断赋值为 \(l\)。那么我们考虑当 \(l >= r - 3eps\) 的时候结束。
例题:https://www.luogu.com.cn/problem/P3382

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define f(i, a, b) for(int i = (a); i <= (b); i++)
#define cl(i, n) i.clear(),i.resize(n);
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<int, int> pii;
const int inf = 1e9;
int n; 
ld eps = 1e-7;
ld a[15];
ld qpow(ld x, int k) {
    ld ans = 1;
    while(k) {
        if(k&1)ans=ans*x;
        x=x*x;
        k>>=1;
    }
    return ans;
}
ld fx(ld x) {
    ld ans = 0;
    f(i, 0, n) {
        ans += qpow(x, i) * a[i];
    }
    return ans;
}
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(NULL);
    cout.tie(NULL);
    //think twice,code once.
    //think once,debug forever.
    cin >> n;
    ld l, r; cin >> l >> r;
    for(int i = n; i >= 0; i--) cin >> a[i];
    while(l + 3 * eps < r) {
        ld mid = (l + r) / 2;
        ld lmid = mid - eps;
        ld rmid = mid + eps;
        if(fx(lmid) > fx(rmid)) r = rmid;
        else l = lmid;
    }
    cout << l << endl;
    return 0;
}

也可以写成 \(r \leftarrow mid, l \leftarrow mid\),但是这样每次会掉 \(eps\) 的精度,那么我们的精度设小一点,\(0.01 \epsilon\) 差不多。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define f(i, a, b) for(int i = (a); i <= (b); i++)
#define cl(i, n) i.clear(),i.resize(n);
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef long double ld;
typedef pair<int, int> pii;
const int inf = 1e9;
int n; 
ld eps = 1e-7;
ld a[15];
ld qpow(ld x, int k) {
    ld ans = 1;
    while(k) {
        if(k&1)ans=ans*x;
        x=x*x;
        k>>=1;
    }
    return ans;
}
ld fx(ld x) {
    ld ans = 0;
    f(i, 0, n) {
        ans += qpow(x, i) * a[i];
    }
    return ans;
}
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(NULL);
    cout.tie(NULL);
    //think twice,code once.
    //think once,debug forever.
    cin >> n;
    ld l, r; cin >> l >> r;
    for(int i = n; i >= 0; i--) cin >> a[i];
    while(l + eps < r) {
        ld mid = (l + r) / 2;
        ld lmid = mid - eps;
        ld rmid = mid + eps;
        if(fx(lmid) > fx(rmid)) r = mid;
        else l = mid;
    }
    cout << l << endl;
    return 0;
}

还有种高级的优化:优选法,但是比较难记就算了。这样写谁都会,不折腾了。

posted @ 2022-08-09 19:21  OIer某罗  阅读(144)  评论(0编辑  收藏  举报