史上最詳盡的斜率優化!

2021-09-29 21:17:25 字數 4636 閱讀 1860

最近被欽定要寫教材,負責斜率優化那一塊,就把寫的內容搬了些上來。

3.6.1斜率優化dp的基本思想

考慮這樣乙個問題:現在要給n個數a[1],a[2]…a[n]分組,每分出一組,你的代價為該組所有數的和的平方+乙個常數m(即設你分出的一組數字是2,3,4,m=5,則你分出該組的代價為(2+3+4)^2+5),求出一種分組方式使得代價總和最小,輸出這個最小的代價和。

如果是最基礎的dp,能夠很顯然地列出來:設dp[i]為將1~i分組的最小代價和,s[i]為a[1]+a[2]..+a[i]的和。dp[i]=min(f[j]+(s[i]-s[j])^2+m),(0<=j

我們想,在轉移時我們要花費大量時間列舉j,這很不划算,我們能否用o(1)或者o(logn)的時間尋找到所有轉移中最優的那個j呢?

我們假設在求解dp[i]的時候,有j,k(j>k)使得從j轉移比從k轉移更優,那麼需要滿足條件:

dp[j]+(s[i]

−s[j])^2+m−

s[k])^2+m

展開上式,移項並消去同類項可得:

(dp[j]

−dp[k]+s[j]^2

−s[k]^2)/(s[j]

−s[k])<2*s[i]

我們設f[j]=dp[j]+s[j]^2,f[k]同理,則可得:(f[j]

−f[k])/(s[j]

−s[k])<2*s[i]

也就是說當j>k時,若有上式,則用j更新dp[i]比用k更新dp[i]優。通過這樣我們已經能夠比較兩個點在轉移i的時候的優劣性了。

現在我們令g(j,k)=(f[j]-f[k])/(s[j]-s[k]),上式即轉換為若g(j,k)<2*s[i],則j比k優,反之k比j優。有乙個結論是:

設x為大於i的乙個點,如果g(j,k)>g(i,j)(k

我們來嘗試證明它:

g(j,k)

和g(i,j)和2*s[x]有三種關係,即g(j,k)>g(i,j)>2*s[x],g(j,k)> 2*s[x]>g(i,j),2*s[x]>g(j,k)>g(i,j),我們分情況討論:對於第一種關係,j比i優,但k比j優,所以j不可能成為最優決策點。對於第二種關係,i比j優,k比j優,所以j不可能成為最優決策點。對於第三種關係,i比j優,j比k優,所以j不可能成為最優決策點。 綜上所述,我們可以斷言:只要存在g(j,k)>g(i,j),(k

我們來想一想怎麼運用這個性質來優化dp。我們建立乙個佇列d表示可能的決策點集合,每次我們算出了dp[i]的值,便可以知道f[i]的值了。我們將i加入這個集合,根據上述的性質,我們可以用i刪去這個集合中的某些點。具體來說,就是刪去所有g(j,k)>g(i,j)的j。那我們難道要列舉每個j,k把它們嘗試刪除嗎?這樣太慢了,我們再來想辦法。

我們每次這樣維護,可以知道將佇列d中的點對映到平面上(以s為橫座標,以f為縱座標),它們依次的連線是下凸的(因為g(j,k)的本質就是斜率),如圖所示:

有著這樣乙個性質,每次我們在佇列末尾插入乙個點i,就只需要檢驗在佇列中i,與i前面乙個位置j,與j前面乙個位置k(這裡所說的位置都是指在佇列中的位置),是否是呈下凸的,如果不呈,則將j刪去後繼續檢驗,直到呈下凸為止(這個用while迴圈可以實現)。

這樣,我們就得到乙個由所有可能決策點所組成的佇列d,如果我們現在要求dp[i],我們應該由佇列d中的哪個來轉移過來呢?

我們先回想一下開始得到的結論:對於k我們現在知道集合d是下凸的,也就是g的值是逐漸遞增的,我們需要尋找乙個最大的g(j,k),使得g(j,k)<2*s[i],那麼這個j就是最優的決策點。證明:因為g(j,k)<2*s[i],所以對於i來說j比k優,因為g遞增,所以對於k後乙個位置l,g(k,l)那麼我們關於斜率優化dp的思想的學習到這裡基本就結束了,我們現在來幫助大家總結一下整個過程,希望以此來幫助大家徹底掌握這個知識點。

1.先列出乙個最基礎的dp;如果這個dp不涉及最小最大值轉移,或對轉移有較多複雜的限制條件,則無法用斜率優化。

2.令k3.得到結論當g(j,k)

4.對於可能的決策點佇列d,根據結論維護上凸或下凸,即每次加入乙個點就刪去一些點。

5.在佇列d中,二分查詢乙個斜率小於s[i]且斜率最大的點(或斜率大於s[i]且斜率最小的點,看不等式符號決定),作為dp[i]的轉移點。特殊地,如果s[i]單調遞增遞減,還可以用單調佇列維護。

3.6.2斜率優化dp的應用

例3.6-1 hdu 3507 print article:

【參考程式】

#include

#define n 100010

int a[n],d[n],s[n],dp[n]; //字母的定義與上文相同

int y(int x,int y)   //將相鄰點對映到平面上的縱座標之差

int x(int x,int y)  //將相鄰點對映到平面上的橫座標之差

int main()

int l=0; int r=0;

for (int i=1;i<=n;i++)

printf("%d\n",dp[n]);

return 0;

}例3.6-2:

[apio2010]

特別行動隊

【題目描述】

你有一支由n名預備役士兵組成的部隊,士兵從1到n編號,要將他們拆分成若干特別行動隊調入戰場。出於默契考慮,同一支特別行動隊中隊員的編號應該連續,即為形如(i,i+1,…,i+k)的序列。

編號為i的士兵的初始戰鬥力為xi,一支特別運動隊的初始戰鬥力x為隊內士兵初始戰鬥力之和,即x=(xi)+(xi+1)+…+(xi+k)。

通過長期的觀察,你總結出一支特別行動隊的初始戰鬥力x將按如下經驗公式修正為x』:x』=ax^2+bx+c,其中a,b,c是已知的係數(a<0)。

作為部隊統帥,現在你要為這支部隊進行編隊,使得所有特別行動隊修正後戰鬥力之和最大。試求出這個最大和。

例如,你有4名士兵,x1=2,x2=2,x3=3,x4=4。經驗公式中的引數為a=-1,b=10,c=-20。此時,最佳方案是將士兵組成3個特別行動隊:第一隊包含士兵1和士兵2,第二隊包含士兵3,第三隊包含士兵4。特別行動隊的初始戰鬥力分別為4,3,4,修正後的戰鬥力分別為4,1,4。修正後的戰鬥力和為9,沒有其它方案能使修正後的戰鬥力和更大。

【輸入格式】

輸入由三行組成。第一行包含乙個整數n,表示士兵的總數。第二行包含三個整數a,b,c,經驗公式中各項的係數。第三行包含n個用空格分隔的整數x1,x2,…,xn,分別表示編號為1,2,…,n的士兵的初始戰鬥力。

【輸出格式】

輸出乙個整數,表示所有特別行動隊修正戰鬥力之和的最大值。

【樣例輸入】

4-1 10 -20

2 2 3 4

【樣例輸出】

9【資料範圍】

20%的資料中,n<=1000;

50%的資料中,n<=10000;

100%的資料中,1<=n<=1000000,-5<=a<=-1,b<=10000000,|c|<=10000000,1<=xi<=100。

【問題分析】

容易寫出dp方程:dp[i]=max(dp[j]+a*(s[i]−s[j])^2+b*(s[i]−s[j])+c)。考慮斜率優化。我們回憶一下上文的步驟,令kdp[k]+a*(s[i]−s[k])^2+b*(s[i]−s[k])+c,令f[x]=dp[x]+a*s[x]^2,則可得(f[j]-f[k])/(s[j]-s[k])>2*a*s[i]+b。令g(j,k)=(f[j]-f[k])/(s[j]-s[k]),則若g(j,k)>2*a*s[i]+b,則對於i來說j比k優,反之相反。可得結論若有g(j,k)

【參考程式】

#include

long long s[1000010],p[1000010];

long long f[1000010];

int d[1000010];

int main()

printf("%lld\n",f[n]);

return 0;

}3.6.3斜率優化習題推薦

1.[hnoi2008] 玩具裝箱

【題目描述】

p教授要去看奧運,但是他捨不下他的玩具,於是他決定把所有的玩具運到北京。他使用自己的壓縮器進行壓縮,其可以將任意物品變成一堆,再放到一種特殊的一維容器中。p教授有編號為1…n的n件玩具,第i件玩具經過壓縮後變成一維長度為c[i].為了方便整理,p教授要求在乙個一維容器中的玩具編號是連續的。同時如果乙個一維容器中有多個玩具,那麼兩件玩具之間要加入乙個單位長度的填充物,形式地說如果將第i件玩具到第j個玩具放到乙個容器中,那麼該容器的長度將為 x=j-i+c[k],(i<=k<=j)

製作容器的費用與容器的長度有關,根據教授研究,如果容器長度為x,其製作費用為(x-l)^2.其中l是乙個常量。p教授不關心容器的數目,他可以製作出任意長度的容器,甚至超過l。但他希望費用最小.

【輸入格式】

第一行輸入兩個整數n,l,第二行輸入所有c[i],1<=n<=50000,1<=l,c[i]<=10^7

【輸出格式】

一行,輸出最小費用

【樣例輸入】

5 43 4 2 1 4

【樣例輸出】

1

史上最詳盡的LCT講解

摘自popoqqq 優秀的學姐 lct能幹嘛 1 維護乙個序列,支援下列操作 區間求和 區間求最值 區間修改 求連續子段和 這個線段樹就可以解決 具體做法不加累述了 2 維護乙個序列,支援下列操作 區間求和 區間求最值 區間修改 求連續子段和 新增一段區間 刪除一段區間 翻轉一段區間 splay的基...

史上最詳盡的平衡樹 splay 講解與模板

首先宣告 萬分感謝gty大哥的幫助!這年頭能找到簡單易懂的陣列版平衡樹模板只能靠學長了!變數宣告 f i 表示i的父結點,ch i 0 表示i的左兒子,ch i 1 表示i的右兒子,key i 表示i的關鍵字 即結點i代表的那個數字 cnt i 表示i結點的關鍵字出現的次數 相當於權值 size i...

史上最牛的面試

1.你為什麼來應聘這份工作。答 以前俺是乙隻迷途的騾子,現在可算找到組織了。2.你是怎麼知道我們招聘這個職位的呢?答 乙個合格的員工除了要有騾子般的身體以外,還必須有獵狗一樣的嗅覺。3.我們為什麼要聘你呢?答 俺吃的少,拉的多。4.你認為自己最大的優點是什麼?答 像騾子一樣吃苦,像工蜂一樣耐勞,像獵...