【筆記篇】單調隊列優化dp學習筆記&&luogu2569_bzoj1855股票交♂易
DP頌
DP之神 聖潔美麗 算法光芒照大地
我們懷著 崇高敬意 跪倒在DP神殿裏
你的復雜 能讓蒟蒻 試圖入門卻放棄
在你光輝 照耀下面 AC真心不容易
dp大概是最經久不衰 亙古不化的算法了吧.
而且有各種各樣的類型 優化之類的.
一直dp都不怎麽好. 而且也不太知道應該怎麽提高.
基本見到不認識的dp方程就不大會推(但我會打表啊= =
所以dp還是很有的學的~
正好最近剛剛肝了計算幾何, 所以就順帶搞一下斜率優化dp一類的...
單調隊列優化dp
單調隊列大家都會吧?
不會的先出去學一下, 這裏不講.
好的, 我們來看一下這個柿子
\[
f[i]=max\{f[j]+\omega(j)\} (j\in[1..i))
\]
其中\(\omega(j)\)是一個費用函數, 一般會根據題目的不同而變化.
這個dp能做到什麽復雜度呢?
首先一眼\(O(n^2)\)...
然而我們可以用一個變量記錄一下之前出現過的最大值.
這樣轉移是\(O(1)\)的了, 總復雜度就降到了\(O(n)\).
但是如果是這樣呢?
\[
f[i]=max\{f[j]+\omega(j)\}(j\in[i-m,i))
\]
那就不能只維護一個變量了, 因為最大值如果出現在\(j\)的取值區間之外則轉移是不合法的.
這樣我們就考慮用單調隊列來維護最大值, 這樣轉移依然可以做到\(O(1)\), 總復雜度\(O(n)\).
看道題: (woc辣雞bzoj給的什麽zz數據範圍, T都沒給怎麽做...)
這題可以寫出這麽一個狀態轉移方程
\[
令f[i][j]表示第i天擁有j支股票的最大收益, \f[i][j]=max\left\{
\begin{matrix}
f[i-w-1][k]-ap[i]*(j-k), (k\in[j-as[i],j)) //買入\f[i-w-1][k]+bp[i]*(k-j),(k\in(j,j+bs[i]]) //賣出\f[i-1][j]//不交♂易
\end{matrix}
\right.
\]
其中不交易的情況好處理, 但是如果前面兩種枚舉\(k\)的話就要做到\(O(n^*maxP^2)\), 顯然是過不了的, 我們必須考慮優化.
我們以買入為栗化一波柿子(因為賣出同理) :
我們令\(\omega(x)=ap[i]*x\), 而我們枚舉\(i\), 就可以視為\(i\)是定值, 於是\(ap[i],as[i]\)都是定值.
我們就可以看出第二維形成了一個能用單調隊列優化的柿子了.
這樣優化之後復雜度成功降到了\(O(n*maxP)\), 就可以通過此題了.
根據貪心原則, 為了獲得最多的現金, 手裏不應該留股票, 所以用每個\(f[i][0]\)更新答案即可.
不過要註意一下邊界條件... 挺扯淡的..
代碼:
#include <cstdio>
#include <cstring>
const int N=2020;
const int INF=0x7fffffff;
int q[N<<1],d[N<<1],h=1,t=0;
int f[N][N],ap[N],bp[N],as[N],bs[N];
inline int gn(int a=0,char c=0){
for(;c<'0'||c>'9';c=getchar());
for(;c>47&&c<58;c=getchar())a=a*10+c-48;return a;
}
inline int max(const int& a,const int& b){
return a>b?a:b;
}
int main(){
int n=gn(),m=gn(),w=gn(),ans=0;
for(int i=1;i<=n;++i)
ap[i]=gn(),bp[i]=gn(),as[i]=gn(),bs[i]=gn();
memset(f,192,sizeof(f));
for(int i=1;i<=n;++i){
for(int j=0;j<=as[i];++j) f[i][j]=-ap[i]*j;
for(int j=0;j<=m;++j) f[i][j]=max(f[i][j],f[i-1][j]);
if(i>w){
h=1; t=0;
for(int j=0;j<=m;++j){
int val=f[i-w-1][j]+j*ap[i];
while(t>=h&&val>=q[t]) --t;
q[++t]=val; d[t]=j;
while(t>=h&&d[h]<j-as[i]) ++h;
f[i][j]=max(f[i][j],q[h]-ap[i]*j);
}
h=1; t=0;
for(int j=m;j>=0;--j){
int val=f[i-w-1][j]+bp[i]*j;
while(t>=h&&val>=q[t]) --t;
q[++t]=val; d[t]=j;
while(t>=h&&d[h]>j+bs[i]) ++h;
f[i][j]=max(f[i][j],q[h]-bp[i]*j);
}
}
ans=max(ans,f[i][0]);
}
printf("%d",ans);
}
反正差不多就這樣吧....
【筆記篇】單調隊列優化dp學習筆記&&luogu2569_bzoj1855股票交♂易