【题意】告诉你 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[] 分别放置窗口里面的最大值以及最大值所在的窗口位置。这里我用了一个结构体的队列直接维护这两个信息!

5. 如何构造单调队列里面的转移,其实很简单例如 dp[3] = max(dp[1] + 2 * v, dp[2] + 1 * v);  两边同时减去 3 * v 就可以变成一个通项公式了,偏移正好。高中数学知识。

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;
}