有t天,第i天的股票买入价格\(AP_i\),数量限制\(AS_i\);卖出价格\(BP_i\),数量限制\(BS_i\)(\(AP_i\geq BP_i\));两次交易之间要相隔w天,所持有股票不能超过maxp,初始无限多的钱,询问可以赚到的最多钱,\(0≤W<T≤2000,1≤MaxP≤2000\)。
解
显然状态中要表现这是第几天,以及持有的股票数,决策是买还是卖,于是设\(f[i][j]\)表示第i天持有股票j所赚最多钱数,注意递推的状态性,也就是只关注第i天持有股票j的所赚钱,会对接下来的理解大有帮助。
注意到一个小问题,\(w=0\)时,就是买和卖显然可以在同一天,但是先买还是先卖,发现调换顺序并无影响,而先买再卖显然不符合生活逻辑,对于第i天,持有股票j而言,假设先买a,再卖b,所持股票变化量为a-b,同样由\(f[i-1][j-(a-b)=j-a+b]\)转移过来,显然钱的变化量\(-a\times AP_i+b\times BP_i\),为什么不直接买\(a-b(a-b\geq 0)\),钱的变化量\(-(a-b)\times AP_i\),或者卖\(b-a,(a-b<0)\),钱的变化量\((b-a)\times BP_i\),根据题意\(AP_i\geq BP_i\)容易知道后式子两个都要大于第一个式子,于是我们对于一天只要考虑它是买还是卖了。
接着不难有
\[f[i][j]=\max\begin{cases}\max_{\max(0,j-AS_i)\leq k<j}\{f[i-1][k]-(j-k)\times AP_i\}\\\max_{j<k\leq \min(maxp,j+BS_i)}\{f[i-1][k]+(k-j)\times BP_i\}\end{cases}\]
即
\[f[i][j]=\max\begin{cases}\max_{\max(0,j-AS_i)\leq k<j}\{f[i-1][k]+k\times AP_i\}-j\times AP_i\\\max_{j<k\leq \min(maxp,j+BS_i)}\{f[i-1][k]+k\times BP_i\}-j\times AP_i\end{cases}\]
边界:\(f[0][0]=0\),其余无限大
答案:\(f[t][0]\)
注意到决策集合范围单调递增,可以使用单调队列,按套路跑两次单调队列即可,时间复杂度不难得知为\(O(t\times maxp)\)。
参考代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#define il inline
#define ri register
#define Size 2050
using namespace std;
int dp[2050][2050],T[2050],L,R,
AS[2050],AP[2050],BS[2050],BP[2050];
il void read(int&);
template<class free>il free Min(free,free);
template<class free>il free Max(free,free);
int main(){
int t,maxp,w;
read(t),read(maxp),read(w);
memset(dp,-2,sizeof(dp)),dp[0][0]=0;
for(int i(1);i<=t;++i)
read(AP[i]),read(BP[i]),read(AS[i]),read(BS[i]);
for(int i(1),j,opt;i<=t;++i){
for(j=0;j<=maxp;++j)dp[i][j]=dp[i-1][j];
opt=i-w-1;if(opt<0)opt=0;L=R=1,T[1]=0;
for(j=1;j<=maxp;++j){
while(L<=R&&T[L]<Max(0,j-AS[i]))++L;
dp[i][j]=Max(dp[i][j],dp[opt][T[L]]+(T[L]-j)*AP[i]);
while(L<=R&&dp[opt][j]+j*AP[i]>=dp[opt][T[R]]+T[R]*AP[i])--R;
T[++R]=j;
}L=R=1,T[1]=maxp;
for(j=maxp-1;j>=0;--j){
while(L<=R&&T[L]>Min(maxp,j+BS[i]))++L;
dp[i][j]=Max(dp[i][j],dp[opt][T[L]]+(T[L]-j)*BP[i]);
while(L<=R&&dp[opt][j]+j*BP[i]>=dp[opt][T[R]]+T[R]*BP[i])--R;
T[++R]=j;
}
}printf("%lld",dp[t][0]);
return 0;
}
template<class free>il free Min(free a,free b){return a<b?a:b;}
template<class free>il free Max(free a,free b){return a>b?a:b;}
il void read(int &x){
x&=0;ri char c;while(c=getchar(),c<'0'||c>'9');
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
}