• 股票交易——单调队列优化DP


    题目描述

     

     思路

      蒟蒻还是太弱了,,就想到半个方程就GG了,至于什么单调队列就更想不到了。

      $f[i][j]$表示第$i天有j$张股票的最大收益。

      那么有四种选择:

    1. 不买股票:$f[i][j]=max(f[i][j],f[i-1][j])$
    2. 买$j$张股票,之前没有买:$f[i][j]=-j*ap[i]$
    3. 买$j$张股票,之前有过交易,中间间隔了$w$天:$f[i][j]=max(f[i][j],f[i-w-1][k]-(j-k)*ap[i])$
    4. 卖$j$张股票,之前有过交易(废话),中间间隔了$w$天,$f[i][j]=max(f[i][j],f[i-w-1][k]+(k-j)*bp[i])$

      方程列出来了,还是很好理解的。那么怎么优化呢?我们发现$i,j,k$都要枚举$O(n^3)$的复杂度是会$T$的,我们需要用一些优化。

      我们发现:3转移中$f[i-w-1][k]-(j-k)*ap[i]=f[i-w-1][k]+k*ap[i]-j*ap[i]$,这样我们在第i天时只要找出最大的$f[i-w-1][k]+k*ap[i]$即可,因为前两维$i,j$已知,$j*ap[i]$是常数。

      用一个递减的单调队列维护使$f[i-w-1][k]+k*ap[i]$最大的$k$,每次取队首转移即可,注意如果$as[i]<j-k$(即买不了这么多股票)时,需要把队首弹出。那么4也是一样,维护最大的$f[i-w-1][k]+k*bp[i](bs[i]<k-j)$

    code

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
    const int N=2010;
    int n,m,w,ap,bp,as,bs;
    int f[N][N],q[N],head,tail;
    
    int main()
    {
        scanf("%d%d%d",&n,&m,&w);
        memset(f,128,sizeof(f));//负无穷 
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d%d%d",&ap,&bp,&as,&bs);
            for(int j=0;j<=as;j++)f[i][j]=-ap*j;//转移2 
            for(int j=0;j<=m;j++)f[i][j]=max(f[i-1][j],f[i][j]);//转移1 
            if(i<=w)continue;//之前不能进行交易 
            head=1,tail=0;
            for(int j=0;j<=m;j++)
            {
                while(head<=tail&&q[head]<j-as)
                head++;//不够买 
                while(head<=tail&&f[i-w-1][q[tail]]+q[tail]*ap<=f[i-w-1][j]+j*ap)
                tail--;//单调性 
                q[++tail]=j;//加入新元素 
                if(head<=tail)
                f[i][j]=max(f[i][j],f[i-w-1][q[head]]+q[head]*ap-j*ap);//转移3 
            }
            head=1,tail=0;
            for(int j=m;j>=0;j--)
            {
                while(head<=tail&&q[head]>j+bs)
                head++;//不够卖 
                while(head<=tail&&f[i-w-1][q[tail]]+q[tail]*bp<=f[i-w-1][j]+j*bp)
                tail--;//单调性 
                q[++tail]=j;//加入新元素 
                if(head<=tail)
                f[i][j]=max(f[i][j],f[i-w-1][q[head]]+q[head]*bp-j*bp);//转移4 
            }
        }
        cout<<f[n][0];//最后一天全部卖出即为最优 
    }
                    
  • 相关阅读:
    索尼MT27i Android2.3.7 线刷Android4.04
    如何在三分钟内要到陌生女孩的电话
    闲情逸致小说嫉妒
    LINQ查询返回DataTable类型
    30个Oracle语句优化规则详解(1)
    .net session超时设置 sessionState的相关属性
    Socket请求和Http请求的各自特点、区别及适用场景
    在Oracle触发器中调用webService 或者java程序
    vs2010中使用log4net的方法
    Oracle 记录操作时长
  • 原文地址:https://www.cnblogs.com/THRANDUil/p/11605570.html
Copyright © 2020-2023  润新知