**自
我們知道,有些dp方程可以轉化成dp[i]=f[j]+x[i]的形式,其中f[j]中儲存了只與j相關的量。這樣的dp方程我們可以用單調佇列進行優化,從而使得o(n^2)的複雜度降到o(n)。
可是並不是所有的方程都可以轉化成上面的形式,舉個例子:dp[i]=dp[j]+(x[i]-x[j])*(x[i]-x[j])。如果把右邊的乘法化開的話,會得到x[i]*x[j]的項。這就沒辦法使得f[j]裡只存在於j相關的量了。於是上面的單調佇列優化方法就不好使了。
這裡學習一種新的優化方法,叫做斜率優化,其實和凸包差不多,下面會解釋。
舉例子說明是最好的!hdu 3507,很適合的乙個入門題。
大概題意就是要輸出n個數字a[n],輸出的時候可以連續連續的輸出,每連續輸出一串,它的費用是 「這串數字和的平方加上乙個常數m」。
我們設dp[i]表示輸出到i的時候最少的花費,sum[i]表示從a[1]到a[i]的數字和。於是方程就是:
dp[i]=dp[j]+m+(sum[i]-sum[j])^2;
很顯然這個是乙個二維的。題目的數字有500000個,不用試了,二維鐵定超時了。那我們就來試試斜率優化吧,看看是如何做到從o(n^2)複雜度降到o(n)的。
分析:我們假設k兩邊移項一下,得到:(dp[j]+num[j]^2-(dp[k]+num[k]^2))/(2*(num[j]-num[k]))那麼不就是yj-yk/xj-xk那麼yj-yk/xj-xk關鍵的來了:現在從左到右,還是設k我們假設g[i,j]如果g[i,j]>=sum[i],那麼j點此時是比i點要更優,但是同時g[j,k]>g[i,j]>sum[i]。這說明還有k點會比j點更優,同樣排除j點。
排除多餘的點,這便是一種優化!
接下來看看如何找最優解。
設k由於我們排除了g[i,j]這樣,從左到右,斜率之間就是單調遞減的了。當我們的最優解取得在j點的時候,那麼k點不可能再取得比j點更優的解了,於是k點也可以排除。換句話說,j點之前的點全部不可能再比j點更優了,可以全部從解集中排除。
於是對於這題我們對於斜率優化做法可以總結如下:
1,用乙個單調佇列來維護解集。
2,假設佇列中從頭到尾已經有元素a b c。那麼當d要入隊的時候,我們維護佇列的上凸性質,即如果g[d,c]=g[x,y]為止,並將d點加入在該位置中。
3,求解時候,從隊頭開始,如果已有元素a b c,當i點要求解時,如果g[b,a]
1 #include2 #includeview code3using
namespace
std;45
int dp[500005];6
int q[500005];7
int sum[500005];8
inthead,tail,n,m;910
int getdp(int i,int
j)11
1415
int getup(int j,int k) //
yj-yk的部分
1619
20int getdown(int j,int k) //
xj-xk的部分
2124
25int
main()
2647 printf("
%d\n
",dp[n]);48}
49return0;
50 }
斜率優化 DP
我們知道,有些dp方程可以轉化成dp i f j x i 的形式,其中f j 中儲存了只與j相關的量。這樣的dp方程我們可以用單調佇列進行優化,從而使得o n 2 的複雜度降到o n 可是並不是所有的方程都可以轉化成上面的形式,舉個例子 dp i dp j x i x j x i x j 如果把右邊...
dp斜率優化
我們知道,有些dp方程可以轉化成dp i f j x i 的形式,其中f j 中儲存了只與j相關的量。這樣的dp方程我們可以用單調佇列進行優化,從而使得o n 2 的複雜度降到o n 可是並不是所有的方程都可以轉化成上面的形式,舉個例子 dp i dp j x i x j x i x j 如果把右邊...
斜率優化DP
斜率優化主要解決的是轉移方程中存在乙個同時與i和j有關的部分時的優化問題 dp i min dp j a i b j 0 include using namespace std typedef long long ll const int maxn 1e5 5 ll a maxn b maxn dp...