定义dp[i][j]表示第i天手中有j股股票时,获得的最多钱数。
转移方程有:
1.当天不买也不卖: dp[i][j]=dp[i-1][j];
2.当天买了j-k股: dp[i][j]=max(dp[r][k]+(j-k)*Ap[i]); (r<i-w)
3.当天卖了k-j股: dp[i][j]=max(dp[r][k]+(k-j)*Bp[i]); (r<i-w)
直接转移复杂度太高,为O(n^2*Maxp^2).
分别考虑每种转移,第一种不用管,考虑第二种。
dp[i][j]=max(dp[r][k]+(j-k)*Ap[i])
变换得:dp[i][j]-j*Ap[i]=dp[r][k]-k*Ap[i]。
所以变成使dp[r][k]-k*Ap[i]最大。
对于dp[r][k]-k*Ap[i], 因为我们已经有dp[i][k]=dp[i-1][k]的转移了,说明,dp[i][k]包含了所有dp[j][k] (j<i)的情况。 换句话说dp[i][k]是递增的。那么这里我们显然就可以直接把r换成i-w-1,于是变成了求 dp[i-w-1][k]-k*Ap[i]的最大值。
令f[k]=dp[i-w-1][k]-k*Ap[i]) 原式变为 dp[i][j]=max(f[k])+j*Ap[i] (0=<k<j),如果再把与i相关的东西变成常数,则变成类似dp[j] = max(f[k])+c[j]形式,即变成可用单调队列优化的形式。
维护一个单调递增队列来求f[k]。复杂度O(n*Maxp).
因为直接令r=i-w-1,因为r>=1,所以i>w+1时才能转移,这是i<=w+1的情况需要预处理。
代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #define Mod 1000000007 using namespace std; #define N 2007 struct node { int num,val; }que[N]; int AP[N],BP[N],AS[N],BS[N]; int dp[N][N]; int n,Maxp,w; void init() { int i,j; for(i=0;i<=2000;i++) for(j=0;j<=Maxp;j++) dp[i][j] = -Mod; dp[0][0] = 0; for(i=1;i<=w+1;i++) for(j=0;j<=min(AS[i],Maxp);j++) dp[i][j] = -j*AP[i]; } int main() { int i,j,k; int t,head,tail; scanf("%d",&t); while(t--) { scanf("%d%d%d",&n,&Maxp,&w); for(i=1;i<=n;i++) scanf("%d%d%d%d",&AP[i],&BP[i],&AS[i],&BS[i]); init(); for(i=1;i<=n;i++) { //unbuy & unsell for(j=0;j<=Maxp;j++) dp[i][j] = max(dp[i][j],dp[i-1][j]); if(i-w-1 <= 0) continue; //buy j-k stocks head = 1; tail = 0; for(j=0;j<=Maxp;j++) { int tmp = dp[i-w-1][j] + j*AP[i]; while(tail >= head && j-que[head].num > AS[i]) head++; if(head <= tail) dp[i][j] = max(dp[i][j],que[head].val-(j-que[head].num)*AP[i]); while(tail >= head && que[tail].val+que[tail].num*AP[i] < tmp) tail--; que[++tail].num = j; que[tail].val = dp[i-w-1][j]; } //sell k-j stocks head = 1; tail = 0; for(j=Maxp;j>=0;j--) { int tmp = dp[i-w-1][j] + j*BP[i]; while(tail >= head && que[head].num-j > BS[i]) head++; if(head <= tail) dp[i][j] = max(dp[i][j],que[head].val-(j-que[head].num)*BP[i]); while(tail >= head && que[tail].val+que[tail].num*BP[i] < tmp) tail--; que[++tail].num = j; que[tail].val = dp[i-w-1][j]; } } int res = 0; for(i=0;i<=Maxp;i++) res = max(res,dp[n][i]); printf("%d\n",res); } return 0; }