有t天,第i天的股票买入价格(AP_i),数量限制(AS_i);卖出价格(BP_i),数量限制(BS_i)((AP_igeq 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 imes AP_i+b imes BP_i),为什么不直接买(a-b(a-bgeq 0)),钱的变化量(-(a-b) imes AP_i),或者卖(b-a,(a-b<0)),钱的变化量((b-a) imes BP_i),根据题意(AP_igeq BP_i)容易知道后式子两个都要大于第一个式子,于是我们对于一天只要考虑它是买还是卖了。
接着不难有
[f[i][j]=maxegin{cases}max_{max(0,j-AS_i)leq k<j}{f[i-1][k]-(j-k) imes AP_i}\max_{j<kleq min(maxp,j+BS_i)}{f[i-1][k]+(k-j) imes BP_i}end{cases}
]
即
[f[i][j]=maxegin{cases}max_{max(0,j-AS_i)leq k<j}{f[i-1][k]+k imes AP_i}-j imes AP_i\max_{j<kleq min(maxp,j+BS_i)}{f[i-1][k]+k imes BP_i}-j imes AP_iend{cases}
]
边界:(f[0][0]=0),其余无限大
答案:(f[t][0])
注意到决策集合范围单调递增,可以使用单调队列,按套路跑两次单调队列即可,时间复杂度不难得知为(O(t imes 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();
}