1. 程式人生 > >poj 1180 dp 斜率優化

poj 1180 dp 斜率優化

單調佇列斜率優化分析:
考慮dp[i],對i<j<k來說,如果保證決策k不比決策j差的條件是:
dp[j]+(s+sumt[i]-sumt[j])*sumf[i]>=dp[k]+(s+sumt[i]-sumt[j])*sumf[i];
整理得:
(dp[k]-dp[j])/(sumt[k]-sumt[j])>=sumf[i];
設:
b[i,x]=dp[x]+(s+sumt[i]-sumt[x])*sumf[i];
g[k,j]=(dp[k]-dp[j])/(sumt[k]-sumt[j])
由上面的式子可知:
b[i,j]>=b[i,k]  <==>  g[k,j]>=sumf[i];   決策k不比決策j差
b[i,j]<b[i,k]  <==>  g[k,j]<sumf[i];    決策j比決策k 優
進而可知:
當i<c<b<a時,如果有g[a,b]>g[b,c],那麼b永遠不會成為dp[i]的決策。

證明:
如果g[a, b] > g[b, c],那麼我們可以分兩個方面考慮g[a, b]與sumF[i]的關係:
(1)如果g[a, b] >= sumF[i],那麼決策a不會比決策b差,也就說決策b不可能是決策點
(2)如果g[a, b] < sumF[i],那麼由於g[a, b] > g[b, c],那麼g[b, c] < sumF[i],那麼決策c要比決策b好,所以b還不能作為決策點 


根據上面的結論和一些特性,我們可以考慮維護一個斜率的佇列來優化整個DP過程:

(1)假設a, b, c依次是佇列尾部的元素,那麼我們就要考慮g[a, b]是否大於g[b, c],如果g[a, b] > g[b, c],那麼可以肯定b一定不會是決策點,所以我們可以從佇列中將b去掉,然後依次向前推,直到找到一個佇列元素少於3個或者g[a, b] <= g[b, c]的點才停止。
(2)假設a, b是依次是佇列頭部的元素,那麼我們知道,如果g[a, b] < sumF[i]的話,那麼對於i來說決策點b肯定優於決策點a,又由於sumF[i]是隨著i減少而遞增的(這個就是為什麼倒推的原因),所以當g[a, b] < sumF[i]時,就一定有g[a, b] < sumF[i-1],因此當前的決策點a不僅僅在考慮dp[i]時不會是最佳決策點,而且在後面的DP中也一定不會是最佳決策點,所以我們可以把a從佇列的頭部刪除,依次往後如此操作,直到佇列元素小於2或者g[a, b] >= sumF[i]。
(3)對於i的更新,一定是佇列頭部的決策點最好,所以O(1)即可轉移。