淺談三分法的原理及應用


最近簡要地學習了三分法這一玄學操作,其實還是比較好理解的。只要多畫畫圖就可以參透。

我們這里以一道經典的模板題來進行講解:P3382 【模板】三分法

我們對於這種凸性函數求最值的問題,一般還是選擇采用三分。

我們先來觀察這種凸性函數(這里以上凸函數為例)

然后我們對於要求最大值的要求,發現如果使用傳統的二分,那么很可能會直接經過最高點,所以我們使用三分。

我們取區間的三等分點,從左到右記作\(lmid\),\(rmid\)

接下來我們對比一下\(f(lmid)\)\(f(rmid)\)的值,若\(f(lmid)<f(rmid)\)那么區間就變為\(lmid\sim r\)否則變為\(l\sim rmid\)

證明的話也比較簡單:

\(lmid\)\(rmid\)在最值的同一側。由於凸性函數在最大值(最小值)任意一側都具有單調性,因此,\(lmid\)\(rmid\)中,更大(小)的那個 數自然更為靠近最值。此時,我們遠離最值的那個區間不可能包含最值,因此可以舍棄。

\(lmid\)\(rmid\)在最值的兩側。由於最值在中間的一個區間,因此我們舍棄一個區間后,並不會影響到最值。

然后對於這一道板子題的話,我們對那個多次函數用秦九韶算法即可\(O(n)\)推導

一般來說,三分法在優化暴力的時候有着比較大的作用,可以直接把一個\(n\)降成\(logn\)

所以在一些很玄學的題目中,我們確定一個函數的值為凸性時,那么三分可能是一個不錯的選擇。

CODE

#include<cstdio>
using namespace std;
typedef double DB;
const int N=15;
const DB EPS=1e-6;
int n;
DB a[N],l,r;
inline DB f(DB x)
{
    DB tot=0;
    for (register int i=n+1;i>=1;--i)
    tot=tot*x+a[n+2-i];
    return tot;
}
int main()
{
    register int i;
    scanf("%d%lf%lf",&n,&l,&r);
    for (i=1;i<=n+1;++i)
    scanf("%lf",&a[i]);
    while (r-l>EPS)
    {
        DB lmid=l+(r-l)/3.0,rmid=r-(r-l)/3.0;
        if (f(lmid)<f(rmid)) l=lmid; else r=rmid;
    }
    printf("%.5lf",l);
    return 0;
}

然后這里推薦一道比較好的題目:HDU3400Line belt&&sol

三分套三分,打板子的技術大有加強。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM