HDU 3401 Trade——单调队列优化dp

题意:给出一种股票n天以来每天的购入价ap和卖出价bp,一开始有无限本金,0张股票,问最多能赚多少钱。

限制:每天只能进行买、卖、什么都不做这三个操作中的一个,每天最多买as张股票,最多卖bs张股票,并且任意一天手里的股票总张数不能超过m张,买卖操作间隔天数必须严格大于w天(比如第10天买了股票,下一次买卖操作必须在1+w+1天或者之后)

思路:dpi】【j】表示第i天手里的股票数为j时的最大收入,容易写出状态转移方程:

什么都不做:dp[i][j] = max{dp[i-1][j]}

买:dp[i][j] = max{dp[i-w-1][k]-(j-k)*ap[i]} = max{dp[i-w-1][k]+k*ap[i]}-j*ap[i];

(max(0,j-as[i])<= k<=j);

卖:dp[i][j] = max{dp[i-w-1][k]+(k-j)*bp[i]} = max{dp[i-w-1][k]+k*bp[i]}-j*bp[i];

(j<=k<=min(m, k+bs[i]));

有了上面的式子,就很容易用单调队列优化了。对于买操作,我们可以想象一个大小为as[i]+1的窗口在dp[i-w+1][]上从左向右滑动;对于卖操作,我们可以想象一个大小为bs[i]+1的窗口在dp[i-w+1][]上从右向左滑动

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 2010;
const int INF = 0x3f3f3f3f;
int T, n, m, w, ap[maxn], bp[maxn], as[maxn], bs[maxn], dp[maxn][maxn];
struct Queue {
    int l, r;
    int p[maxn], q[maxn];
    void init() { l = 1, r = 1; }
    void push(int x, int pos) {
        while (l < r && x > q[r-1]) r--;
        r++;
        p[r-1] = pos;
        q[r-1] = x;
    }
    void pop(int x, int flag) {
        if (flag == 1) while (l < r && p[l] < x) l++;
        else if (flag == 2) while (l < r && p[l] > x) l++;
    }
    int front() { return q[l]; }
}q1, q2;
int main() {
    scanf("%d", &T);
    while (T--) {
        scanf("%d%d%d", &n, &m, &w);
        for (int i = 1; i <= n; i++) scanf("%d%d%d%d", &ap[i], &bp[i], &as[i], &bs[i]);
        for (int i = 0; i <= n; i++) for (int j = 0; j <= m; j++) dp[i][j] = -INF;
        dp[0][0] = 0;
        for (int i = 1; i <= w+1; i++) {
            for (int j = 0; j <= as[i]; j++) {
                dp[i][j] = -ap[i]*j;
            }
        }
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= m; j++) dp[i][j] = max(dp[i][j], dp[i-1][j]);
            if (i <= w+1) continue;
            q1.init(); q2.init();
            for (int j = 0; j <= m; j++) {
                q1.push(dp[i-w-1][j]+j*ap[i], j);
                q1.pop(j-as[i], 1);
                dp[i][j] = max(dp[i][j], q1.front()-j*ap[i]);
            }
            for (int j = m; j >= 0; j--) {
                q2.push(dp[i-w-1][j]+j*bp[i], j);
                q2.pop(j+bs[i], 2);
                dp[i][j] = max(dp[i][j], q2.front()-j*bp[i]);
            }
        }
        int ans = -INF;
        for (int i= 0; i <= m; i++) ans = max(ans, dp[n][i]);
        printf("%d\n", ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/hao_zong_yin/article/details/80013074