BZOJ1855 || P2569 [SCOI2010]股票交易【单调队列优化DP】

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/niiick/article/details/82952649

Time Limit: 5 Sec
Memory Limit: 64 MB

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能赚到的最多的钱数。

HINT

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


题目分析

d p [ i ] [ j ] dp[i][j] 表示到第 i i 天时手上恰好有 j j 张股票的最大收益
那么有状态转移方程

dp[i][j]=-j*ap[i]//赋初值
dp[i][j]=max(dp[i][j],dp[i-1][j])//第i天啥也不干
dp[i][j]=max(dp[i][j],dp[i-w-1][k]-(j-k)*ap[i])//第i天买入股票,(j-as[i]<=k<j)
dp[i][j]=max(dp[i][j],dp[i-w-1][k]+(k-j)*bp[i])//第i天卖出股票,(j<k<=j+bs[i])

直接转移复杂度为 O ( n 3 ) O(n^3) ,显然无法承受
尝试把第3、4个方程中k的枚举省去
以第3个为例
d p [ i ] [ j ] = m a x ( d p [ i ] [ j ] , d p [ i w 1 ] [ k ] ( j k ) a p [ i ] ) = m a x ( d p [ i ] [ j ] , m a x ( d p [ i w 1 ] [ k ] + k a p [ i ] ) j a p [ i ] ) dp[i][j]=max(dp[i][j],dp[i-w-1][k]-(j-k)*ap[i])=max(dp[i][j],max(dp[i-w-1][k]+k*ap[i])-j*ap[i])

这里面的 m a x ( d p [ i w 1 ] [ k ] + k a p [ i ] ) max(dp[i-w-1][k]+k*ap[i]) 显然可以用单调队列维护
对于第4个方程同理,不过 j j 的枚举要倒序

这样复杂度就降到 O ( n 2 ) O(n^2)


#include<iostream>
#include<cmath>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long lt;
   
int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}
 
const int maxn=2010;
int n,m,w;
int ap[maxn],bp[maxn];
int as[maxn],bs[maxn];
int dp[maxn<<1][maxn<<1],ans;
int q[maxn],ll,rr;
 
int main()
{
    n=read();m=read();w=read();
    for(int i=1;i<=n;++i)
    ap[i]=read(),bp[i]=read(),
    as[i]=read(),bs[i]=read();
     
    memset(dp,128,sizeof(dp)); dp[0][0]=0;
    for(int i=1;i<=n;++i)
    {
        for(int j=0;j<=as[i];++j) dp[i][j]=-1*j*ap[i];
        for(int j=0;j<=m;++j) dp[i][j]=max(dp[i][j],dp[i-1][j]);
         
        if(i-w-1<0) continue;
        ll=rr=1;
        for(int j=0;j<=m;++j)
        {
            while(ll<rr&&q[ll]<j-as[i]) ll++;
            while(ll<rr&&dp[i-w-1][q[rr-1]]+q[rr-1]*ap[i]<=dp[i-w-1][j]+j*ap[i]) rr--;
            q[rr++]=j;
            dp[i][j]=max(dp[i][j],dp[i-w-1][q[ll]]-(j-q[ll])*ap[i]);
        }
          
        ll=rr=1;
        for(int j=m;j>=0;--j)//注意这里倒叙
        {
            while(ll<rr&&q[ll]>j+bs[i]) ll++;
            while(ll<rr&&dp[i-w-1][q[rr-1]]+q[rr-1]*bp[i]<=dp[i-w-1][j]+j*bp[i]) rr--;
            q[rr++]=j;
            dp[i][j]=max(dp[i][j],dp[i-w-1][q[ll]]+(q[ll]-j)*bp[i]);
        }
    }
    for(int i=0;i<=m;++i)
    ans=max(ans,dp[n][i]);
    printf("%d",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/niiick/article/details/82952649
今日推荐