1. 程式人生 > >動態規劃專題(五)——斜率優化DP

動態規劃專題(五)——斜率優化DP

前言

斜率優化\(DP\)是難倒我很久的一個演算法,我花了很長時間都難以理解。後來,經過無數次的研究加以對一些例題的理解,總算啃下了這根硬骨頭。


基本式子

斜率優化\(DP\)的式子略有些複雜,大致可以表示成這樣:

\[f_i=min_{j=1}^{i-1}(A(j)-B(j)*S(i)+C(i))\]

其中\(A(j)\)\(B(j)\)是兩個只與\(j\)有關的函式,\(S(i)\)\(C(i)\)是兩個只與\(i\)有關的函式,式子中的\(min\)其實也可以替換成\(max\),但這裡以\(min\)為例。

不難發現,如果只有\(A(j)\)\(C(i)\)兩項,就是

單調佇列優化\(DP\)的基本式子了。

但是,由於式子中含有\(B(j)*S(i)\)這一項既與\(i\)相關,又與\(j\)相關的式子,就不能直接用單調佇列,而要進行一定轉化了。

考慮將\(A(j)\)移到等號左邊,並將\(f_i\)移到等號右邊,則原式可以轉化成這樣:

\[A(j)=B(j)*S(i)+(f_i-C(i))\]

注意,在\(i\)不變的時候,我們可以將只與\(i\)有關的項看成常數項。

於是,這個函式就可以看作一條直線,其中\(S(i)\)就相當於這條直線的斜率,而\(f_i-C(i)\)就相當於這條直線的截距

\(C(i)\)是固定的,因此,如果要讓\(f_i\)

最小,則應讓\(f_i-C(i)\)最小,對應到影象中就是讓截距最小。

那麼應如何讓截距最小呢?


大致思路

首先,我們可以想象有一條斜率固定的直線(我太懶,不想畫圖... ...),然後圖上有若干個點,現在要用這條直線從圖的最下方往上慢慢移動,直至碰到第一個點,而這個點就是我們要找的最優點。

則不難發現,如果連續的三個點呈上凸狀,則無論該直線斜率取多少,碰到的第一個點都不可能是中間這個點。

換句話說,就是中間這個點對答案沒有任何貢獻了。

於是就有一個策略:當我們要加入一個新的點時,比較當前點與前一個插入的點\(S_1\)、前一個插入的點與倒數第二個插入的點的斜率\(S_2\),如果\(S_1\le S_2\)

,則可將前一個插入的點彈出。

重複此操作,直至\(S_1>S_2\)或圖上只剩一個點,然後將當前點插入。


如何求最優解

但是,這樣一來,我們好像還是沒能求出最優解。

此時又有兩種操作方式:在凸包上二分單調佇列維護最優解

對於某些問題,它可以確保決策單調性,即一個點如果當前不是最優解,則以後都不可能是最優解了。這樣的問題可以直接用單調佇列來維護最優解。

但有些問題卻不一定滿足這種性質,此時就需要在凸包上二分最優解了,但依然需要用單調佇列來維護點集。

所以,如果你不會單調佇列,最好趕緊去研究一下,然後再學習斜率優化\(DP\)


幾道例題

第一道例題: 【BZOJ2726】[SDOI2012] 任務安排

聽說是入門題?洛谷上的弱化版可以直接單調佇列維護,但是\(BZOJ\)上存在負數,需要在凸包上二分。

第二道例題: 【CF311B】Cats Transport

一眼看上去像是\(WQS\)二分,其實題意轉換後可以直接斜率優化。

第三道例題: 【洛谷3648】[APIO2014] 序列分割

一開始覺得是區間\(DP\),結果一看資料範圍,推了波性質,才發現其實可以用斜率優化\(DP\)來做。