<font size=4> ## what is 三分法 對於二分,相信你必定十分熟悉。就是在一個具備單調性序列上查找你所須要的數字。因爲其單調性,你每一次在查找是就能夠將規模縮小一半,大體就是: 1.假設這個數列單調遞增 2.維護一個區間左端點$l$,區間右端點r和中間點$mid$ 3.若是$mid$比想要的值小,則左邊確定不能夠,那麼$l=mid$ 2.若是$mid$比想要的值大,則右邊確定不能夠,那麼$r=mid$ 所以大體就能夠這麼寫: ``` while (l+1<r) { int mid=l+r>>1; if (v<a[mid) r=mid; else l=mid; } ``` 不保證代碼正確,可是具體思想就是這樣。 三分也同樣啊: 對於一段拋物線(極值的一邊單調遞增,極值的一邊單調遞減)咱們就能夠把它分紅三段,根據其圖像特性來求解。c++
對於三分,咱們用左端點$lmid$和$rmid$進行維護,將這個圖像分紅三段。而且圖像區間的左右端點分別是$l<r.$則咱們能夠選擇這麼考慮:(以二次函數$y=-5x^{2}+8x-1$爲例) 如圖所示: 當$lmid$處於$A$點,$rmid$處於$B$點時:可將左端點$l$縮到$lmid$,右端點不變以保證極值存在。 當$lmid$處於$A$點,$rmid$處於$C$點時:照樣能夠將$l$縮到$lmid$。 同理, 當$lmid$處於$C$點,$rmid$處於$D$點時:可將右端點$r$縮到$rmid$,左端點不變以保證極值存在。 當$lmid$處於$B$點,$rmid$處於$B$點時:照樣能夠將$r$縮到$rmid$。 故獲得結論: $$f(l)<f(r)→l=lmid$$ $$f(l)≥f(r)→r=rmid$$ 而後就進行簡單的代碼實現:函數
#include<bits/stdc++.h> using namespace std; double a,b,c; inline double f(double x) { return a*x*x+b*x+c; } int main(void) { cin>>a>>b>>c; //形如y=ax^2+by+c的二次函數 double l=-1e9,r=1e9; while (l+1e-9<r) { double lmid=l+(r-l)/3.0; //圖像上位於1/3部分的靠左的mid值 double rmid=l+(r-l)/3.0*2.0; //圖像上位於2/3部分的靠右的mid值 if (f(lmid)<f(rmid)) l=lmid; else r=rmid; //求單峯極值 } cout<<"X="<<l<<'\n'; cout<<"Y="<<f(l); }
經過畫圖和分類討論$a<0$的狀況,不可貴出: $$f(l)>f(r)→l=lmid$$ $$f(l)≤f(r)→r=rmid$$ 代碼實現只要if內反一下便可:spa
#include<bits/stdc++.h> using namespace std; double a,b,c; inline double f(double x) { return a*x*x+b*x+c; } int main(void) { cin>>a>>b>>c; //形如y=ax^2+by+c的二次函數 double l=-1e9,r=1e9; while (l+1e-9<r) { double lmid=l+(r-l)/3.0; //圖像上位於1/3部分的靠左的mid值 double rmid=l+(r-l)/3.0*2.0; //圖像上位於2/3部分的靠右的mid值 if (f(lmid)>f(rmid)) l=lmid; else r=rmid; //求單峯極值 } cout<<"X="<<l<<'\n'; cout<<"Y="<<f(l); }
若是須要高次函數過其它圖像,只要在f內稍做修改便可。code