• BZOJ1855 [Scoi2010]股票交易[单调队列dp]


    题  

    题面有点复杂,不概括了。


    后面的状态有前面的最优解获得大致方向是dp。先是瞎想了个$f[i][j]$表示第$i$天手里有$j$张股票时最大收入(当天无所谓买不买)。

    然后写了一个$O(n^4)$状转

    $f[i][j]=max(max{f[k][l]-(j-l)*AP[i]},max{f[k][l]+(l-j)*BP[i]})$

    这个很明显就是某一天的前w天之前是可以交易后推到这一天的,因为$i-w~i-1$这些天的状态你也不知道最优解有没有在当天进行交易。那就分为买和卖股票两部分,分别算一下最大利益再合并就行,注意保留不买也不卖的状态。买和卖每天的数量限制就转化为范围。注意题目的意思,一天也是可以既买入又卖出股票的,但是可以发现我们有一部分股票可算作卖出又买回来,这样坑定要亏,那不如不卖了,所以每天要不然买,要不然卖,要不然什么也不做

    验证正确性,写了个40pts暴力

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<cstring>
     5 #include<cmath>
     6 #include<queue>
     7 #define dbg(x) cerr<<#x<<"="<<x<<endl
     8 using namespace std;
     9 typedef long long ll;
    10 template<typename T>inline char MIN(T&A,T B){return A>B?A=B,1:0;}
    11 template<typename T>inline char MAX(T&A,T B){return A<B?A=B,1:0;}
    12 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
    13 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
    14 template<typename T>inline T read(T&x){
    15     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1;
    16     while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x;
    17 }
    18 const int N=2000+7;const ll INF=1e13;
    19 ll f[N][N],ans;
    20 int n,m,w,as,bs,ap,bp;
    21 
    22 int main(){//freopen("test.in","r",stdin);//freopen("tmp.out","w",stdout);
    23     read(n),read(m),read(w);
    24     for(register int i=1;i<=m;++i)f[0][i]=-INF;
    25     for(register int i=1;i<=n;++i){
    26         read(ap),read(bp),read(as),read(bs);
    27         for(register int j=0;j<=m;++j){
    28             f[i][j]=-INF;
    29             for(register int k=0;k<=_max(0,i-w-1);++k){
    30                 for(register int l=_max(0,j-as);l<=j;++l)if(f[k][l]^-INF)MAX(f[i][j],f[k][l]-(j-l)*1ll*ap);
    31                 for(register int l=j+1;l<=_min(m,j+bs);++l)if(f[k][l]^-INF)MAX(f[i][j],f[k][l]+(l-j)*1ll*bp);
    32             }
    33             MAX(ans,f[i][j]);
    34         }
    35     }
    36     printf("%lld
    ",ans);
    37     return 0;
    38 }
    View Code

    好下面想优化。观察到每一行的每一格算卖出的最大利润可以有前面一些行从这一列开始往后的一块选max转移过来,所以卖出可以倒推dp。我可以分列转移,每一列$l$可行的$k∈[1~i-w-1]$天内找一个$max{f[k][l]+(l-j)*b}$即$max{f[k][l]}+(l-j)*b$,那我只需要每列维护一个单调栈,保持栈底是j列的f最大值,然后每列直接转移.复杂度$O(n^3)$。再看我每一格是由后面的$bs[i]$格所在列转移的,每列我已经找到一个最优解,那下面我要从这几列(范围内)内找$max{f[l]+(l-j)*b}$($f[l]$为l这一列最大f值)即$max{f[l]+l*b}-j*b$,这时就可以用单调队列维护max内那玩意儿了。每行做一次,买入转态同理,从前往后做一遍。同样注意不买不卖的情况应予以保留。复杂度就成了$O(n^2)$。细节看垃圾代码

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<algorithm>
     4 #include<cstring>
     5 #include<cmath>
     6 #include<queue>
     7 #define dbg(x) cerr<<#x<<"="<<x<<endl
     8 using namespace std;
     9 typedef long long ll;
    10 template<typename T>inline char MIN(T&A,T B){return A>B?A=B,1:0;}
    11 template<typename T>inline char MAX(T&A,T B){return A<B?A=B,1:0;}
    12 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
    13 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
    14 template<typename T>inline T read(T&x){
    15     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1;
    16     while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x;
    17 }
    18 const int N=2000+7;const ll INF=1e13;
    19 ll f[N][N],ans;
    20 int q[N],qb[N][N],qa[N][N],ta[N],tb[N];//t--stack's top
    21 int n,m,w,a,b,A,B,l,r;//a--buy  b--sell  A--buying Bmax stocks  B--selling Smax stocks
    22 inline ll Fb(int j,int b){return f[qb[j][1]][j]+1ll*j*b;}
    23 inline ll Fa(int j,int a){return f[qa[j][1]][j]+1ll*j*a;}
    24 
    25 int main(){//freopen("test.in","r",stdin);//freopen("tmp.out","w",stdout);
    26     read(n),read(m),read(w);
    27     for(register int j=0;j<=m;++j)f[0][j]=-INF;f[0][0]=0;
    28     for(register int i=1;i<=n;++i){
    29         read(a),read(b),read(A),read(B);
    30         for(register int j=0;j<=m;++j)f[i][j]=-INF;
    31         l=1,r=0;
    32         for(register int j=m;~j;--j){
    33             if(i>w+1){
    34                 while(tb[j]&&f[qb[j][tb[j]]][j]<=f[i-w-1][j])--tb[j];
    35                 qb[j][++tb[j]]=i-w-1;
    36             }
    37             while(l<=r&&Fb(q[r],b)<=Fb(j,b))--r;
    38             q[++r]=j;
    39             while(l<=r&&q[l]>j+B)++l;
    40             MAX(f[i][j],Fb(q[l],b)-1ll*j*b);
    41         }
    42         l=1,r=0;
    43         for(register int j=0;j<=m;++j){
    44             if(i>w+1){
    45                 while(ta[j]&&f[qa[j][ta[j]]][j]<=f[i-w-1][j])--ta[j];
    46                 qa[j][++ta[j]]=i-w-1;
    47             }
    48             while(l<=r&&Fa(q[r],a)<=Fa(j,a))--r;
    49             q[++r]=j;
    50             while(l<=r&&q[l]<j-A)++l;
    51             MAX(f[i][j],Fa(q[l],a)-1ll*j*a);
    52             MAX(ans,f[i][j]);
    53         }
    54     }
    55     printf("%lld
    ",ans);
    56     return 0;
    57 }

    嗯上边的思路是我个人想的,有部分地方想繁掉了,于是A掉之后看了一眼题解,发现自己真傻。。单调栈完全没必要开出来的。

    我要找每一列的合法最大值转移,不用单调栈维护,只要在$f[i-w-1][j]$直接从$f[i-w-2][j]$继承过来就好了,也不会影响合法性,我前一天做了交易,今天不动,所以没事,这样我在这里就保留了j这一列最大f值,因为它来自于上面数行中$fmax$和当前行dp取得的f的最大值。所以一开始朴素方程就是$O(n^3)$的。这个继承获得之前最大值的办法希望记住。

  • 相关阅读:
    git revert和git reset的区别
    git merge 和 git rebase 小结
    Markdown——入门指南
    使用Git Wiki 管理文档时,文档编写的基本用法
    mysql 列 默认添加 dedault null ??
    redis aof文件过大问题
    redis源码分析——aofrewrite
    改变文章的字号大小
    推荐大家使用的CSS书写规范及顺序
    inline-block 前世今生
  • 原文地址:https://www.cnblogs.com/saigyouji-yuyuko/p/10454439.html
Copyright © 2020-2023  润新知