【题意】告诉你 T 天里,物品的买卖价格,和最多买卖数量,还有 T 天里能买入最多的物品数量,每一次买卖间隔时间必须大于 w+1!
【分析】
1. dp[i][j] 表示前 i 天手里有 j 支股票时所获得的最大收益,因为要间隔 w+1 天才能进行一次买卖,所以前 w+1 天只能进行一个操作,那就是买操作。
2. 关于赋初值问题,合法的状态也有可能为负值的,而且结果要取最大值,所以非法状态要赋值为 -INFS.
3. dp[i][j] = max(dp[i-w-1][x] - (j-x)*AP, dp[i-w-1][y] + (y-j)*BP); 另外还有第 i 天什么都不做的情况此时为 dp[i][j] = max(dp[i][j], dp[i-1][j]).
4. 关于转移方程,时间复杂度是很高的,所以要采取单调队列优化,deq[],pos[] 分别放置窗口里面的最大值以及最大值所在的窗口位置。这里我用了一个结构体的队列直接维护这两个信息!
6.推到队列优化也可以这样做,只考虑买的情况,卖的和它对称。dp[i][j] = max(dp[i-w-1][k]-j*ap[i]+k*ap[i]),移下项变成dp[i][j]+j*ap[i] = max(dp[i-w-1][k]+k*ap[i]),很明显可以把后面维护成一个递减的单调队列,每次取队首元素,使得复杂度降为O(N*P)!
【AC代码】
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 2200;
const int unf = -0x3f3f3f3f;
int N,P,W,dp[maxn][maxn],ap[maxn],bp[maxn],as[maxn],bs[maxn];
//dp[i][j]代表在第i天拥有j股的最大价值
struct node{
int val,pos;
}que[maxn];
int main()
{
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d%d",&N,&P,&W);
for(int i=1; i<=N; i++){
scanf("%d%d%d%d",&ap[i],&bp[i],&as[i],&bs[i]);
}
//init.
memset(dp,unf,sizeof(dp));
for(int i=1; i<=N; i++){
for(int j=0; j<=min(as[i],P); j++){
dp[i][j] = -ap[i]*j;
}
}
//在第天什么也不做
for(int i=2; i<=N; i++){
for(int j=0; j<=P; j++){
dp[i][j] = max(dp[i][j],dp[i-1][j]);
}
}
//状态转移,维护dp[i][j]
int head,tail,pre;
for(int i=W+2; i<=N; i++){
pre = i-W-1;
head = 0,tail = -1;
for(int j=0; j<=P; j++){//only buy
dp[i][j] = max(dp[i][j],dp[i-1][j]);
node temp;
temp.val = dp[pre][j]+j*ap[i];
temp.pos = j;
//maintain queue.
while(head<=tail && que[tail].val<temp.val) tail--;
que[++tail] = temp;
while(head<=tail &&(j-que[head].pos)>as[i]) head++;
if(head<=tail){
dp[i][j] = max(dp[i][j],que[head].val-j*ap[i]);
}
}
head = 0,tail = -1;
for(int j=P; j>=0; j--){//only sell
dp[i][j] = max(dp[i][j],dp[i-1][j]);
node temp;
temp.val = dp[pre][j]+j*bp[i];
temp.pos = j;
//maintain queue.
while(head<=tail && que[tail].val<temp.val) tail--;
que[++tail] = temp;
while(head<=tail &&(que[head].pos-j)>bs[i]) head++;
if(head<=tail){
dp[i][j] = max(dp[i][j],que[head].val-j*bp[i]);
}
}
}
printf("%d\n",dp[N][0]);//在最后一天卖完股票的最大值
}
return 0;
}