斜率优化初步
use:https://imgse.com/
引言(瞎扯),聽_rqy大佬說斜率優(yōu)化更應該叫做截距優(yōu)化
rqy大佬說的好像很有道理qwq。
斜率優(yōu)化,概括的來講就是優(yōu)化形如\(f_i=a_i+Max^{i-1}_{j=0}(y_j-k_ix_j)\)其中\(k_i\)是遞減的(或\(Max\)換成\(Min\),\(k_i\)是遞增的)的一種方法
其中\(a_i\)是一個和\(j\)無關(guān)的常數(shù),然后我們看\(Max\)中,就只有\(k_i\)是與\(j\)無關(guān)的常數(shù)
如果暴力\(dp\)的話,時間復雜度就是\(O(N^2)\),我們還需考慮優(yōu)化
狀態(tài)是\(O(N)\)的,已經(jīng)夠優(yōu)了。考慮優(yōu)化轉(zhuǎn)移到\(O(1)\)。
我們將方程中的\(y_j,x_j\)拿出來,看做點\((x_j,y_j)\),然后放到笛卡爾坐標系中(嘿,真洋呼)
像這樣
然后將直線\(y=kx+b\)帶入
然后我們選取一點,算出將直線\(y=kx+b\)平移至改點,然后算出平移后直線的截距
\(y-y_i=k(x-x_i)\)
\(y=kx+y_i-kx_i\)截距就是\(y_i-kx_i\)。發(fā)現(xiàn)就是所需的數(shù)值
然后我們揭下來就是利用其加速決策.
假設我們在進行\(k_i\)時的決策,我們,我們可以找到一條\(y=kx\)的直線,然后在我們1~i點組成的圖形中找到一個截距最大的點,就可以進行轉(zhuǎn)移了。很巧的是,這條符合條件的直線只經(jīng)過這個點,不經(jīng)過其他點之間之間的連線(或只有多個點)
such as
然后我們還有個性質(zhì),\(k_i\)是遞減的,然后我們在進行決策時候,就可以讓上圖中紅線的繞這個圖形進行旋轉(zhuǎn),隨著\(i\)的增加,逐漸轉(zhuǎn)到綠線所到的位置。
就像這樣(ps:作圖時沒考慮好qwq
然后我們就會發(fā)現(xiàn),有些點是我們考慮都不會考慮的,如下圖所示
這一部分我們就丟掉不管了,反正他不是最優(yōu)的。
然后我們發(fā)現(xiàn)我們進行完這一步,就好像凸出來一塊。然后我們按照如此規(guī)則,修整一下整個圖形
這個東西叫做凸殼
到此,我們先回憶一下。如何利用凸殼進行決策
然后對于一個\(i\),我們先取得\(k_i\),然后再從凸殼\(1-i\)中找到一條直線\(y=kx+b\),使得其截距最大。
而所找到的直線不會穿過凸殼內(nèi)部。
凸殼是怎么來的呢?在\(k\)遞減的情況下,繞所有點所圍成的。(就像墻上一堆釘子,拿一根小木棍繞著轉(zhuǎn),決定小木棍的方向的是最外邊的兩個點,而不是其內(nèi)部的點,而最外面的點相鄰的,對小木棍方向有決定的點連線,形成的圖形就是凸殼(馬馬虎虎qwq))
好,然后我們再來考慮維護凸殼,以及如何具體使用凸殼
如何維護插入一個點,使用單調(diào)隊列,在我們改變直線斜率時,也就是旋轉(zhuǎn)直線時,如果是順時針旋轉(zhuǎn),則不用干其他的操作。如果是逆時針旋轉(zhuǎn),則需要進行一些維護操作
如下圖
需要我們介入,將\(C\)點刪除,那如何程序?qū)崿F(xiàn)呢?
先不急,我們再來看一個更極端的栗子。
我們?nèi)绾尾僮髂?#xff1f;
貪心的想,我們只比較\(E,F\)兩個點,然后我們分別算一下新增點到\(E,F\)兩條直線的斜率,若發(fā)現(xiàn)\(F\)的斜率比\(E\)的直線的斜率大,那我們就刪除\(F\),\(E\)點何時刪除呢?
在我們比較\(D,E\)是刪除,所以我們就使用單調(diào)隊列,里面是斜率單減的。
void push(int i)//將第i個點加入 {while(tail-head>=1)//隊列中至少要留兩個元素,另外,因為我比較毒瘤,所以我的隊首和隊尾都是實指{int a=queue[tail],b=queue[tail-1];//去出后兩個元素if((y[i]-y[a])*(x[i]-x[b])>(x[i]-x[a])*(y[i]-y[b]))//比較,聽_rqy說是叉積//好高級的樣子,其實就是兩個截距式比較(我也不知道叫什么,必修二一點也沒學,其實讀者將左右對調(diào)對調(diào),成為分數(shù)的形式,就能看出來為什么了。寫成乘法快,還可以避免爆精)tail--;else break;}queue[++tail]=i; }然后就是取隊首
暴力的想,每次按遍歷所有點,取最大值
然后發(fā)現(xiàn),竟然是單峰呀。然后我們再來改變斜率(遞減),發(fā)現(xiàn)峰值所在的位置竟然遞增
好好好,單調(diào)隊列qwq。
其實這里你可以想象,一條直線就真的在凸殼上旋轉(zhuǎn),然后就會有個支點,使得這條直線繞他旋轉(zhuǎn),這個點就是當時直線截距最大的點。然后總是要離開這個支點的,然后到了下一個支點,下一個支點就是在另一個斜率時的峰頂qwq
void pop(int i) {while(tail-head>=1)//需要保存兩個點{int a=queue[head],b=queue[head+1];//取出前兩個值if(y[a]-k[i]*x[a]<y[b]-k[i]*x[b])//保持單調(diào)性,排除掉不是最優(yōu)解head++;else break;}return ; }end.
轉(zhuǎn)載于:https://www.cnblogs.com/Lance1ot/p/9418936.html
總結(jié)
- 上一篇: 一名优秀的clickbaitor必备的技
- 下一篇: 苹果手机双卡双待是哪一款_手机双卡双待信