[bzoj1855][DP]股票交易

123 篇文章 1 订阅

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

题解

n4 n 4 的dp很显然,设 f[i][j] f [ i ] [ j ] 表示第i天拥有j的股票的最大收益,对于p有 p=[0,max(0,iW1)] p = [ 0 , m a x ( 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 − A S i <= k < j m a x ( f [ p ] [ k ] − ( j − k ) ∗ A P i )

f[i][j]=j<k<=j+BSimax(f[p][k]+(kj)BPi) f [ i ] [ j ] = ∑ j < k <= j + B S i m a x ( f [ p ] [ k ] + ( k − j ) ∗ B P i )

考虑如何优化,此处仅考虑对于购买式,卖出式同样
即为 =max(f[p][k]+kAPi)jAPi = m a x ( f [ p ] [ k ] + k ∗ A P i ) − j ∗ A P i
由于 f[p][k] f [ p ] [ k ] p p 单调递增的情况下单调不减
发现对于任意k都可以容易维护一个最大值f[p][k]+kAPi
复杂度降为 nmaxP2 n ∗ m a x P 2
又发现对于任意j,j-ASi单调递增
故可以单调队列优化
再次降为 nmaxP n ∗ m a x P

#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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值