[BZOJ1855][Scoi2010]股票交易(DP+单调队列优化)

本文介绍了一种基于动态规划的股票交易策略算法,通过优化状态转移方程来提高计算效率,利用单调队列维护最大值,实现了O(T×MaxP)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

f[i][j]f[i][j]表示到了第ii天,手里有j张股票,赚到最多的钱。
如果第ii天不进行交易,则f[i][j]可以从f[i1][j]f[i−1][j]转移。即:

f[i][j]=f[i1][j]f[i][j]=f[i−1][j]

如果第ii天进行交易,则f[i][j]可以从f[max(0,iW1)][...]f[max(0,i−W−1)][...]转移。
(下面设p=max(0,iW1)p=max(0,i−W−1)
设存在非负整数k,hk,h满足k>hk>h
判断h×BPik×APih×BPi−k×APi(kh)×APi−(k−h)×APi的大小关系。
把右边拆开得到h×APik×APih×APi−k×APi,由于APiBPiAPi≥BPih>0h>0
因此(kh)×APih×BPik×APi−(k−h)×APi≥h×BPi−k×APi
k<hk<h时同理。因此得出:在最优情况下,一天要么不进行交易,要么只买入,要么只卖出。
所以得出第ii天进行交易时的转移:
f[i][j]=max(f[i][j],maxjASik<j{f[p][k](jk)×APi})

f[i][j]=max(f[i][j],maxj<kBSi+j{f[p][k]+(kj)×BPi})f[i][j]=max(f[i][j],maxj<k≤BSi+j{f[p][k]+(k−j)×BPi})

将等号右边maxmax里的式子拆开,得到:
maxjASik<j{f[p][k]+k×APi}j×APimaxj−ASi≤k<j{f[p][k]+k×APi}−j×APi

maxj<kBSi+j{f[p][k]+k×BPi}j×BPimaxj<k≤BSi+j{f[p][k]+k×BPi}−j×BPi

复杂度O(T×MaxP2)O(T×MaxP2)
考虑到jASij−ASiBSi+jBSi+jjj单调递增,
因此可以用单调队列分别维护f[p][k]+k×APif[p][k]+k×BPif[p][k]+k×BPi的最大值。复杂度O(T×MaxP)O(T×MaxP)
代码:
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
const int N = 2005, INF = 0x3f3f3f3f;
int T, maxP, W, AP[N], BP[N], AS[N], BS[N], f[N][N], He, Ta, Q[N];
bool ex[N][N];
int main() {
    int i, j; T = read(); maxP = read(); W = read();
    for (i = 1; i <= T; i++) AP[i] = read(), BP[i] = read(),
        AS[i] = read(), BS[i] = read(); ex[0][0] = 1;
    for (i = 1; i <= T; i++) {
        for (j = 0; j <= maxP; j++) f[i][j] = -INF;
        for (j = 0; j <= maxP; j++) if (ex[i - 1][j])
            f[i][j] = f[i - 1][j], ex[i][j] = 1; int p = max(0, i - W - 1);
        He = Ta = 0; for (j = 0; j <= maxP; j++) {
            while (He < Ta && j - Q[He + 1] > AS[i]) He++;
            if (He != Ta) f[i][j] = max(f[i][j], f[p][Q[He + 1]]
                - (j - Q[He + 1]) * AP[i]), ex[i][j] = 1;
            if (ex[p][j]) {
                while (He < Ta && f[p][Q[Ta]] + Q[Ta] * AP[i] <
                    f[p][j] + j * AP[i]) Ta--; Q[++Ta] = j;
            }
        }
        He = Ta = 0; for (j = maxP; j >= 0; j--) {
            while (He < Ta && Q[He + 1] - j > BS[i]) He++;
            if (He != Ta) f[i][j] = max(f[i][j], f[p][Q[He + 1]]
                + (Q[He + 1] - j) * BP[i]), ex[i][j] = 1;
            if (ex[p][j]) {
                while (He < Ta && f[p][Q[Ta]] + Q[Ta] * BP[i] <
                    f[p][j] + j * BP[i]) Ta--; Q[++Ta] = j;
            }
        }
    }
    int ans = -INF; for (i = 0; i <= maxP; i++)
        if (ex[T][i]) ans = max(ans, f[T][i]); cout << ans << endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值