zoukankan      html  css  js  c++  java
  • [SCOI2010]股票交易(单调队列优化dp)

    [SCOI2010]股票交易

    题目描述

    最近lxhgww又迷上了投资股票,通过一段时间的观察和学习,他总结出了股票行情的一些规律。

    通过一段时间的观察,lxhgww预测到了未来T天内某只股票的走势,第i天的股票买入价为每股APi,第i天的股票卖出价为每股BPi(数据保证对于每个i,都有APi>=BPi),但是每天不能无限制地交易,于是股票交易所规定第i天的一次买入至多只能购买ASi股,一次卖出至多只能卖出BSi股。

    另外,股票交易所还制定了两个规定。为了避免大家疯狂交易,股票交易所规定在两次交易(某一天的买入或者卖出均算是一次交易)之间,至少要间隔W天,也就是说如果在第i天发生了交易,那么从第i+1天到第i+W天,均不能发生交易。同时,为了避免垄断,股票交易所还规定在任何时间,一个人的手里的股票数不能超过MaxP。

    在第1天之前,lxhgww手里有一大笔钱(可以认为钱的数目无限),但是没有任何股票,当然,T天以后,lxhgww想要赚到最多的钱,聪明的程序员们,你们能帮助他吗?

    输入输出格式

    输入格式:

    输入数据第一行包括3个整数,分别是T,MaxP,W。

    接下来T行,第i行代表第i-1天的股票走势,每行4个整数,分别表示APi,BPi,ASi,BSi。

    输出格式:

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

    输入输出样例

    输入样例#1:

    5 2 0
    2 1 1 1
    2 1 1 1
    3 2 1 1
    4 3 1 1
    5 4 1 1

    输出样例#1:

    3

    说明

    对于30%的数据,0<=W<T<=50,1<=MaxP<=50

    对于50%的数据,0<=W<T<=2000,1<=MaxP<=50

    对于100%的数据,0<=W<T<=2000,1<=MaxP<=2000

    对于所有的数据,1<=BPi<=APi<=1000,1<=ASi,BSi<=MaxP

    前面的东西就不再赘述了,直接上转移方程。

    状态转移方程:

    1 . 凭空买

    仅本情况下的状态可以直接赋值,其他状态初值为 (-inf),即:

    [F[i,j]=-api*j ]

    [(0<=j<=asi) ]

    下面三种情况,分别可列三个状态转移方程:

    2 . 不买也不卖

    最好想也最好列转移方程的一种情况,直接由上一天转移,且拥有股票数量不变。即:

    [F[i,j]=max(F[i,j],F[i-1,j]) ]

    3 . 在之前的基础上买股票

    这里的方程就比较复杂了,这里详细解释一下:

    题目中指明说两次交易需要至少隔 (w) 天。也就是说,今天是第 i天交易,那么上一次交易最近是在第(i-w-1) 天。

    可我第(i-w-1) 天之前也可以交易啊?为什么偏偏是那一天,而不是第(i-w-2)天?

    回看刚刚讨论的第 2 种情况,我们已经把某一天以前的最优答案转移到了该天,所以从那一天转移,相当于从那一天包括前面任何一天开始转移,省去了大把时间。

    接下来,我们已知第 (i)天拥有 (j)张股票,假设第(i-w-1)天拥有 (k)张股票。

    (k)一定比 (j) 小,因为这一种情况是买股票,股票只能多。

    但股票又不能买太多,限制最多为 (as) 张。所以 (j - aSi) 是底线。

    继续,这次交易买了 (j - k)张股票,要花去((j-k)*aPi)元。

    整理一下,转移方程为:

    [F[i,j]=max(F[i,j],F[i-w-1,k]-(j-k)*aPi)$ $$(j-aSi<=k<j)]

    4 . 在之前的基础上卖股票

    和上面的情况特别类似,在此简单地说一下区别。

    因为是卖股票, (k) 这次要比 (j)大。但要小于等于 (j + bSi)

    这次交易卖了 (k - j)张邮票,赚得((k - j)*bPi)元。

    整理一下,转移方程为:

    [F[i,j]=max(F[i,j],F[i-w-1,k]+(k-j)*bPi) ]

    [(j<k<=j+bSi) ]

    这个时候发现那两种情况,实际上可以使用单调性优化。此时达到降时间复杂度的目标。

    就以第 3 种情况,在之前的基础上买股票为例子吧。

    再重复提一下那个转移方程:

    [F[i,j]=max(F[i,j],F[i-w-1,k]-(j-k)*aPi) ]

    [(j-aSi<=k<j) ]

    运用乘法分配率:

    [F[i,j]=max(F[i,j],F[i-w-1,k]-j*aPi+k*aPi) ]

    [(j-aSi<=k<j) ]

    既然要将状态转移给F[i,j]此时j可以从 max中提取出,此时转移方程变为:

    [F[i,j]=max(F[i,j],F[i-w-1,k]+k*aPi)-j*aPi ]

    [(j-aSi<=k<j) ]

    此时,我们发现以上的转移方程符合单调性优化的条件,故可以使用单调性优化。

    还有一个细节,是第 3 种情况转移应该顺序,第 4 种情况转移应该逆序,这个不难理解,先自己想想吧。

    其实就是,在考虑卖股票的时候,持有的股票越多是不是当前值会越大,我们为了使队首为最优解(即最大值),倒序即可。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<string>
    #include<algorithm>
    using namespace std;
    int read()
    {
        int x=0,w=1;char ch=getchar();
        while(ch>'9'||ch<'0') {if(ch=='-')w=-1;ch=getchar();}
        while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
        return x*w;
    }
    int dp[2010][2010],team1[2010],team2[2010];
    struct node{
    int ap,bp,as,bs;
    }a[2010];
    int main()
    {
        memset(dp,-0x7f,sizeof(dp));
        int t=read(),maxx=read(),w=read();
        for(int i=1;i<=t;i++)
        {
            a[i].ap=read();a[i].bp=read();
            a[i].as=read();a[i].bs=read();
        }
        for(int i=1;i<=t;i++)
        {
            for(int j=0;j<=a[i].as;j++)
            dp[i][j]=-a[i].ap*j;
            for(int j=0;j<=maxx;j++)
            {
                dp[i][j]=max(dp[i][j],dp[i-1][j]);
            }
            if(i-w<=0) continue;
            int l1=1,r1=0,l2=1,r2=0;
            for(int j=0;j<=maxx;j++)
            {
                while(l1<=r1&&dp[i-w-1][team1[r1]]+a[i].ap*team1[r1]<=dp[i-w-1][j]+a[i].ap*j) r1--;
                team1[++r1]=j;
                while(l1<=r1&&j-team1[l1]>a[i].as) l1++;
                if(l1<=r1)
                dp[i][j]=max(dp[i][j],dp[i-w-1][team1[l1]]-a[i].ap*(j-team1[l1]));
            }
            for(int j=maxx;j>=0;j--)
            {
                while(l2<=r2&&dp[i-w-1][team2[r2]]+a[i].bp*team2[r2]<=dp[i-w-1][j]+a[i].bp*j) r2--;
                team2[++r2]=j;
                while(l2<=r2&&team2[l2]-j>a[i].bs) l2++;
                if(l2<=r2)
                dp[i][j]=max(dp[i][j],dp[i-w-1][team2[l2]]+a[i].bp*(team2[l2]-j));
            }
            /*for(int j=0;j<=maxx;j++)
            {	
                if(j<=a[i].as)
                dp[i][j]=-1*a[i].ap*j;
                dp[i][j]=max(dp[i][j],dp[i-1][j]);
                for(int k=0;k<=maxx;k++)
                {
                    if(j>k&&j-k<=a[i].as&&i-w-1>0)
                    dp[i][j]=max(dp[i][j],dp[i-w-1][k]-a[i].ap*(j-k));
                    else if(j<k&&k-j<=a[i].bs&&i-w-1>0)
                    dp[i][j]=max(dp[i][j],dp[i-w-1][k]+a[i].bp*(k-j));
                }
            }*/
        }
        cout<<dp[t][0];
    }
    
  • 相关阅读:
    对象访问方式
    GC回收的对象
    邮件工具类
    java内存区域
    RabbitMQ的安装
    Flask信号
    DBUtils数据库连接池
    Flask蓝图基本使用
    Flask中使用cookie和session
    Flask中的CBV
  • 原文地址:https://www.cnblogs.com/lsgjcya/p/9072022.html
Copyright © 2011-2022 走看看