[bzoj1855][DP]股票交易

本文介绍了一个复杂的股票交易策略问题,通过动态规划的方法进行求解,实现了时间复杂度的有效降低。

Description

最近lxhgww又迷上了投资股票,通过一段时间的观察和学习,他总结出了股票行情的一些规律。
通过一段时间的观察,lxhgww预测到了未来T天内某只股票的走势,第i天的股票买入价为每股APi,第i天的股票卖出价为每股BPi(数据保证对于每个i,都有APi>=BPi),但是每天不能无限制地交易,于是股票交易所规定第i天的一次买入至多只能购买ASi股,一次卖出至多只能卖出BSi股。
另外,股票交易所还制定了两个规定。为了避免大家疯狂交易,股票交易所规定在两次交易(某一天的买入或者卖出均算是一次交易)之间,至少要间隔W天,也就是说如果在第i天发生了交易,那么从第i+1天到第i+W天,均不能发生交易。同时,为了避免垄断,股票交易所还规定在任何时间,一个人的手里的股票数不能超过MaxP。
在第1天之前,lxhgww手里有一大笔钱(可以认为钱的数目无限),但是没有任何股票,当然,T天以后,lxhgww想要赚到最多的钱,聪明的程序员们,你们能帮助他吗?

Input

输入数据第一行包括3个整数,分别是T,MaxP,W。
接下来T行,第i行代表第i-1天的股票走势,每行4个整数,分别表示APi,BPi,ASi,BSi。

Output

输出数据为一行,包括1个数字,表示lxhgww能赚到的最多的钱数。

Sample Input

5 2 0

2 1 1 1

2 1 1 1

3 2 1 1

4 3 1 1

5 4 1 1

Sample Output

3

HINT

对于30%的数据,0 < =W 对于50%的数据,0 < =W 对于100%的数据,0 < =W 对于所有的数据,1 < =BPi <
=APi < =1000,1 < =ASi,BSi < =MaxP

题解

n4n4的dp很显然,设f[i][j]f[i][j]表示第i天拥有j的股票的最大收益,对于p有p=[0,max(0,iW1)]p=[0,max(0,i−W−1)],有转移方程

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

f[i][j]=jASi<=k<jmax(f[p][k](jk)APi)f[i][j]=∑j−ASi<=k<jmax(f[p][k]−(j−k)∗APi)

f[i][j]=j<k<=j+BSimax(f[p][k]+(kj)BPi)f[i][j]=∑j<k<=j+BSimax(f[p][k]+(k−j)∗BPi)

考虑如何优化,此处仅考虑对于购买式,卖出式同样
即为=max(f[p][k]+kAPi)jAPi=max(f[p][k]+k∗APi)−j∗APi
由于f[p][k]f[p][k]pp单调递增的情况下单调不减
发现对于任意k都可以容易维护一个最大值f[p][k]+kAPi
复杂度降为nmaxP2n∗maxP2
又发现对于任意j,j-ASi单调递增
故可以单调队列优化
再次降为nmaxPn∗maxP
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
using namespace std;
struct node{int APi,BPi,ASi,BSi;}a[2005];
int T,mxP,W;
int li[2005],g[2005];
int mx[2005],f[2005][2005];
bool vis[2005][2005];
/*
f[p][k]
max(l1)         max(l2)
*/
int h,t;
int main()
{
    scanf("%d%d%d",&T,&mxP,&W);
    for(int i=1;i<=T;i++)scanf("%d%d%d%d",&a[i].APi,&a[i].BPi,&a[i].ASi,&a[i].BSi);
    int p;memset(f,-63,sizeof(f));
    memset(vis,false,sizeof(vis));
    f[0][0]=0;vis[0][0]=true;
    for(int i=1;i<=T;i++)
    {
        for(int j=0;j<=mxP;j++)if(vis[i-1][j])f[i][j]=f[i-1][j],vis[i][j]=true;
        p=max(0,i-W-1);
        h=t=1;li[1]=0;
        for(int j=0;j<=mxP;j++)
        {
            while(h<t && j-a[i].ASi>li[h])h++;
            if(h<=t && j-a[i].ASi<=li[h])f[i][j]=max(f[i][j],f[p][li[h]]-(j-li[h])*a[i].APi),vis[i][j]=true;
            if(vis[p][j])
            {
                while(h<=t && f[p][j]+j*a[i].APi>f[p][li[t]]+li[t]*a[i].APi)t--;
                li[++t]=j;
            }
        }
        h=t=1;li[1]=mxP;
        for(int j=mxP;j>=0;j--)
        {
            while(h<t && li[h]>j+a[i].BSi)h++;
            if(h<=t && j+a[i].BSi>=li[h])f[i][j]=max(f[i][j],f[p][li[h]]+(li[h]-j)*a[i].BPi),vis[i][j]=true;
            if(vis[p][j])
            {
                while(h<=t && f[p][j]+j*a[i].BPi>f[p][li[t]]+li[t]*a[i].BPi)t--;
                li[++t]=j;
            }
        }
    }
    int ans=0;
    for(int i=0;i<=mxP;i++)ans=max(ans,f[T][i]);
    printf("%d\n",ans);
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值