[SCOI 2010]股票交易

Description

题库链接

一共 \(t\) 天,第 \(i\) 天股票买入价 \(ap_i\),最多可买 \(as_i\) 股;卖出价 \(bp_i\),最多可买 \(bs_i\) 股。并且在任何时候你最多持有 \(maxp\) 股股票,相邻两次交易(买入或卖出)要间隔 \(w+1\) 天。默认初始你有无限的钱,问 \(t\) 天后你最多能赚多少钱。

\(1\leq w< t\leq 2000,1\leq maxp\leq 2000\)

Solution

首先默认 \(w = w+1\)

\(f_{i,j}\) 表示第 \(i\) 天持有 \(j\) 张股票时,获得的最大收益。

考虑买入,\(f_{i,j}=\max\limits_{j-k\leq as_i}f_{i-w,k}+(k-j)\times ap_i=-j\times ap_i+\max\limits_{j-k\leq as_i}f_{i-w,k}+k\times ap_i\)。发现 \(j\) 这一维可以单调队列优化。

卖出同理,综上复杂度为 \(O(t\times maxp)\)

Code

#include <bits/stdc++.h>
using namespace std;
const int N = 2000+5, inf = 1e9;

int t, maxp, w, ap[N], bp[N], as[N], bs[N], f[N][N];
int q[N], head, tail;

int main() {
    scanf("%d%d%d", &t, &maxp, &w); ++w;
    for (int i = 1; i <= t; i++)
        scanf("%d%d%d%d", &bp[i], &ap[i], &bs[i], &as[i]);
    for (int i = 1; i <= maxp; i++) f[0][i] = -inf;
    for (int i = 1; i <= w; i++) {
        for (int j = 1; j <= bs[i]; j++)
            f[i][j] = max(-bp[i]*j, f[i-1][j]);
        for (int j = bs[i]+1; j <= maxp; j++)
            f[i][j] = max(-inf, f[i-1][j]);
    }
    for (int i = w+1; i <= t; i++) {
        q[head = tail = 1] = 0;
        for (int j = 1; j <= maxp; j++) {
            while (j-q[head] > bs[i]) ++head;
            f[i][j] = f[i-w][q[head]]+(q[head]-j)*bp[i];
            while (head <= tail && f[i-w][q[tail]]+q[tail]*bp[i] < f[i-w][j]+j*bp[i]) --tail;
            q[++tail] = j;
        }
        q[head = tail = 1] = maxp;
        for (int j = maxp-1; j >= 0; j--) {
            while (q[head]-j > as[i]) ++head;
            f[i][j] = max(f[i-w][q[head]]+(q[head]-j)*ap[i], f[i][j]);
            while (head <= tail && f[i-w][q[tail]]+q[tail]*ap[i] < f[i-w][j]+j*ap[i]) --tail;
            q[++tail] = j;
        }
        for (int j = 0; j <= maxp; j++) f[i][j] = max(f[i][j], f[i-1][j]);
    }
    int ans = 0;
    for (int i = 0; i <= maxp; i++) ans = max(ans, f[t][i]);
    printf("%d\n", ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/NaVi-Awson/p/12238958.html