[周末训练]股票交易

题目

【内存限制:$512MiB$】【时间限制:$1000ms$】
【标准输入输出】【题目类型:传统】【评测方式:文本比较】

【题目描述】

原题来自:SCOI 2010

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

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

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

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

【输入格式】

输入数据第一行包括三个整数,分别是 $T,\text{MaxP},W$。

接下来 $T$ 行,第 $i$ 行代表第 $i-1$ 天的股票走势,每行四个整数,分别表示 $AP_i,BP_i,AS_i,BS_i$。

【输出格式】

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

【样例】

样例输入

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

样例输出

3

【数据范围与提示】

对于 $30\%$ 的数据,$0\le W\lt T\le 50,1\le \text{MaxP}\le 50$;
对于 $50\%$ 的数据,$0\le W\lt T\le 2000,1\le \text{MaxP}\le 50$;
对于 $100\%$ 的数据,$0\le W\lt T\le 2000,1\le \text{MaxP}\le 2000,1\le BP_i\le AP_i\le 1000,1\le AS_i,BS_i\le \text{MaxP}$。

题解

【做题经历】

看到输入的时候我被吓到了......

因为对于我这种蒟蒻来说,就只是这道题的输入的定义,我就可以被绕晕。

但是仔细思考了一下,感觉这道题的思路其实是比较明显的,$DP$的方法显而易见

然后......我就开始了暴搜......

【正解】

先挂一个大佬的博客:orz

一道思路明显的$DP$优化题。

先对状态进行说明:$dp[i][j]$:在第 $i$ 天持有 $j$ 张股票时的最大收益。

首先,我们应该对信息相对于以往比较多的题目进行分析。

题意我不必概括,我们来说一说操作:

  • 买入操作:可以在第 $i$ 天买入股票,每个股票的花费为 $AP_i$,并且最多不能买超过 $AS_i$
  • 卖出操作:可以在第 $i$ 天卖出股票,每个股票的售价为 $BP_i$,并且最多不能卖超过 $BS_i$
  • 偷懒操作:你的游戏情结蠢蠢欲动,然后你就和朋友们 $open black$ 去了,并且在股票上你啥都没干......

对于每一天,只可能会有这三种操作情况之一。并且前两种情况必须时隔 $W$ 天。

我们再对这三种情况展开:

  1. 在啥都没有的情况下买入股票:这种情况相当于初始化,此时 $dp[i][j]=-j×ap[i](j \in [0,as[i]])$
  2. 开黑去了,啥也没干:可以直接转移 $dp[i][j]=max\{dp[i][j],dp[i-1][j]\}$
  3. 在啥都没有的情况下卖出股票:你在想什么?货都没有还想交易?这种情况是不可能的,直接忽略。
  4. 在有股票时买股票:这种情况,我们需要对于天数进行讨论。首先,这种情况只需要从第 $i-W-1$ 天转移,那么问题来了,为什么不需要从第 $i-W-2$ 天,或者是更前面的天数转移呢?那是因为我们在第二种情况中,已经将前面所有的最优的情况往前面转移了,所以第 $i-W-1$ 天的状态实际上已经包含了前面的最优的状态了。并且,我们枚举的 $k$ 必须比 $j$ 小,因为我们这是买入股票。然后,$k$ 不能小于 $j-as_i$,因为我们在这一天最多买 $as_i$ 张股票,如果 $k<j-as_i$,也就是说即使是将能买的股票全买了都没有 $j$ 张多,那么怎么转移呢?所以就有了状态:$dp[i][j]=max\{dp[i][j],dp[i-W-1][k]-(j-k)×ap_i|k \in [j-as_i,j)\}$
  5. 在有股票时卖股票:这种情况与上一种很像,此处不再赘述,可自行思考,直接给出状转:$dp[i][j]=max\{dp[i][j],d[i-W-1][k]+(k-j)×bp_i|k \in (j,j+bs_i]\}$

对于上面的四种情况手动忽略第三种,我们有了四个状转,那么可以直接码代码,但是再一想,这个算法复杂度似乎在 $O(TP^2)$,对于这道题是肯定过不了的,考虑进行优化。

看看状转,我们发现这是区间最大值,单点修改,这是可行的思路,并且也可以过,但是或许太过于复杂。

考虑现有的 $DP$ 优化策略,结合其区间最大值转移,很容易想到单调队列,然后代码就出来了......

#include<bits/stdc++.h>
using namespace std;
template<class T>inline void qread(T& x){
    char c;bool f=false;x=0;
    while((c=getchar())<'0'||'9'<c)if(c=='-')f=true;
    for(x=(c^48);'0'<=(c=getchar())&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    if(f)x=-x; 
}
template<class T,class... Args>inline void qread(T& x,Args&... args){qread(x),qread(args...);}
inline int rqread(){
    char c;bool f=false;int x=0;
    while((c=getchar())<'0'||'9'<c)if(c=='-')f=true;
    for(x=(c^48);'0'<=(c=getchar())&&c<='9';x=(x<<1)+(x<<3)+(c^48));
    return f?-x:x;
}
template<class T>inline T Max(const T x,const T y){return x>y?x:y;}
template<class T>inline T Min(const T x,const T y){return x<y?x:y;}
template<class T>inline T fab(const T x){return x>0?x:-x;}
const int MAXT=2000;
int T,P,W,ap,bp,as,bs,dp[MAXT+5][MAXT+5];
int Q[MAXT+5],tail,head;
signed main(){
    qread(T,P,W);
    memset(dp,128,sizeof dp);
    for(int i=1;i<=T;++i){
        qread(ap,bp,as,bs);
        for(int j=0;j<=as;++j)dp[i][j]=-j*ap;
        for(int j=0;j<=P;++j)dp[i][j]=Max(dp[i][j],dp[i-1][j]);
        if(i<=W)continue;
        head=1,tail=0;
        for(int j=0;j<=P;++j){
            while(head<=tail&&Q[head]<j-as)++head;
            while(head<=tail&&dp[i-W-1][Q[tail]]+Q[tail]*ap<=dp[i-W-1][j]+j*ap)--tail;
            Q[++tail]=j;
            if(head<=tail)dp[i][j]=Max(dp[i][j],dp[i-W-1][Q[head]]+Q[head]*ap-j*ap);
        }
        head=1,tail=0;
        for(int j=P;j>=0;--j){
            while(head<=tail&&Q[head]>j+bs)++head;
            while(head<=tail&&dp[i-W-1][Q[tail]]+Q[tail]*bp<=dp[i-W-1][j]+j*bp)--tail;
            Q[++tail]=j;
            if(head<=tail)dp[i][j]=Max(dp[i][j],dp[i-W-1][Q[head]]+Q[head]*bp-j*bp);
        }
    }
    int ans=-0x7f7f7f7f;
    for(int i=0;i<=P;++i)ans=Max(ans,dp[T][i]);
    printf("%d\n",ans);
    return 0;
}

 

posted @ 2019-12-11 19:57  南枙向暖  阅读(127)  评论(0编辑  收藏  举报