股票交易 (单调队列优化DP)

股票交易

ZOjDi9.png



\(solution:\)

这道题以前就写了,题目很好,但自己没有发题解,来补一篇:

首先,题目出得很有迷惑性,但我们不难想到状态要设天数,和自己手上的股票数目(因为这两个就是充要信息)。而我们转移也比较常规,跟着题意模拟就行:

  1. (不买不卖):\(f[i][j]=f[i-1][j]\)
  2. (买入):\(f[i][j]=max\{~\sum_{p=1}^{p<i} \sum_{q=0}^{q<j} f[p][q]-(j-q)*AP[i]~\}\)
  3. (卖出):\(f[i][j]=max\{~\sum_{p=1}^{p<i} \sum_{q=0}^{q>j} f[p][q]+(q-j)*BP[i]~\}\)

但是我们发现这样转移是 \(n^4\) 的。因为我们每一个 \(f[i][j]\) 需要往前枚举 \(p\) ,然后因为股票可以买入卖出,还要枚举一个\(q\) 。就是每一个\(f[i][j]\) 都要完全枚举所有的 \(f[p][q]\) 来转移。但是我们可以发现我们的 \(p\) 是完全可以不用枚举,因为第一个转移保证了 \(f[i-1]\) 就是最优的。于是转移变成了 \(n^3\) 但是数据范围告诉我们这样还不够。

  1. (买入):\(f[i][j]=max\{~\sum_{k=1}^{k<j} f[i-1][k]-(j-k)*AP[i]~\}\)
  2. (卖出):\(f[i][j]=max\{~\sum_{k=1}^{k>j} f[i-1][k]+(k-j)*BP[i]~\}\)

这两个式子其实把括号拆开后就是一个式子:

\(f[i][j]=max\{~\sum_{k=1}^{k>j} f[i-1][k]+k*P[i]-j*P[i]~\}\)

单调队列优化: 我们发现我们买入卖出的式子是一个变量单调递增的,我们的 \(i\) 是最外层循环,在内层循环里它相当于定值,而我们的k和j在式子中是独立的,完全可以用单调队列维护 \(f[i-1][k]+k*P[i]\) ,而 \(-j*P[i]\) 就是定值,只需要来两次单调队列分别对应 \(AP[i]\)\(BP[i]\) 即可。



\(code:\)

#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>

#define ll long long
#define db double
#define inf 0x7fffffff
#define rg register int
#define max(A,B) (A>B?A:B)

using namespace std;

int n,m,w,l,r,now,a,b,c,d,ans;
int f[2001][2001],t[2001],p[2001];

inline int qr(){
    char ch;
    while((ch=getchar())<'0'||ch>'9');
    int res=ch^48;
    while((ch=getchar())>='0'&&ch<='9')
        res=res*10+(ch^48);
    return res;
}

int main(){
    //freopen("1.in","r",stdin);
    //freopen(".out","w",stdout);
    n=qr(),m=qr(),w=qr();
    for(rg i=0;i<=n;++i)
        for(rg j=0;j<=m;++j)
            f[i][j]=-inf;
    for(rg i=1;i<=n;++i){
        a=qr(),b=qr();
        c=qr(),d=qr();
        for(rg j=0;j<=c;++j)
            f[i][j]=-1*j*a;
        for(rg j=0;j<=m;++j)
            f[i][j]=max(f[i][j],f[i-1][j]);
        if(i<=w)continue;
        l=1;r=0;
        for(rg j=0;j<=m;++j){
            now=f[i-w-1][j]+j*a;
            while(l<=r&&p[l]<j-c)++l;
            while(l<=r&&t[r]<=now)--r;
            t[++r]=now;p[r]=j;
            if(l <= r)f[i][j]=max(f[i][j],t[l]-j*a);
        }
        l=1;r=0;
        for(rg j=m;j>=0;--j){
            now=f[i-w-1][j]+j*b;
            while(l<=r&&p[l]>j+d)++l;
            while(l<=r&&t[r]<=now)--r;
            t[++r]=now;p[r]=j;
            if(l <= r)f[i][j]=max(f[i][j],t[l]-j*b);
        }
    }
    for(rg j=0;j<=m;++j)
        ans=max(ans,f[n][j]);
    printf("%d\n",ans);
    return 0;
}

转载于:https://www.cnblogs.com/812-xiao-wen/p/11207164.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值