• CDOJ1696 吴神的炒股技巧 [dp + 单调队列优化]


    题目地址;http://acm.uestc.edu.cn/problem.php?pid=1696&cid=167

    状态定义:dp[i][j]表示第 i 天,手持 j 股时最多能赚多少钱。

    状态转移:第i天交易的状态只能由[0, i-w-1]天的状态转移过来!!!处理方法:构造a[]数组,a[j]表示第[0, i-w-1]天中,手持j股的最大收益,在转移第w+1天到最后一天时,每转移完一天后,更新a[]数组。第i天的状态dp[i][j]的转移根据a[]数组来,而不是dp[i-1][](其实你会发现第i天时,a[j]就是dp[i-w-1][j])。

    优化:单调队列。

    详细细节见代码的注释哟!

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<string>
    #include<cmath>
    #include<iostream>
    #include<algorithm>
    #include<map>
    #include<vector>
    //#include<queue>
    #define sf scanf
    #define pf printf
    #define pfn printf("\n");
    #define ll long long
    #define INF 0x7fffffff
    
    using namespace std;
    
    int t,mp,w,dp[2000][2000],a[2000],deque1[2000],deque2[2000],front1,rear1,front2,rear2;
    int s[2000],b[2000],sp[2000],bp[2000],ans;
    
    int max3(int x1,int x2,int x3){
        if(x1<x2) x1=x2;
        if(x1<x3) x1=x3;
        return x1;
    }
    
    void ini(){
        for(int i=0; i<=t; i++)
            for(int j=0; j<=mp+1; j++)
                dp[i][j] = -INF;          //-INF表示未定义
    }
    
    void DP(){
        //先定义4个变量,后面会解释作用。。
        int toplimit,t1,t2,t3;
        //construct the initial state,前w天内只能买入或什么不做,没的卖。
        for(int i=1; i<=w; i++){
            for(int j=0; j<=mp; j++){   //最多手持mp股哟
                if(j<=b[i])             //最多买b[i]股哟
                    dp[i][j] = -bp[i]*j;
                if(dp[i][j]<dp[i-1][j])
                    dp[i][j]=dp[i-1][j]; //如果前一天值更大,说明吴神在[1,i-1]天中可以以更低价格买到j股
            }
        }
        //a[]数组是个很重要的数组,这里做个说明:
        //1. a[]数组会在处理完一天后更新一次(第w+1天至第t天);
        //2. 处理第i天时,a[j]表示第1天到第i-w天内(即第i天交易的上一次可能交易时间),手持j股时的最大收益。
        //3. 每处理完一天,a[]数组根据第i+1-w天更新。
    
        for(int j=0; j<=mp; j++) //初始化a[]数组
            a[j]=dp[1][j];
    
        //state trans
        for(int i=w+1; i<=t; i++){
            //construct the deque2,构造单调队列只是为了优化
            //deque2是这一次交易,要卖出股票的情况。
            front1=rear1=front2=rear2=0;
            toplimit=min(mp,s[i]);
            for(int j=1; j<=toplimit; j++){
                if(a[j]==-INF) continue;
                while(rear2>front2 && a[j]+(j-deque2[rear2-1])*sp[i] >= a[deque2[rear2-1]]) rear2--;
                deque2[rear2++]=j;
            }
    
            //第i天手持j股的最大收益就是按下面转移来的
            for(int j=0; j<=mp; j++){
                //t1表示这次是卖出,卖出后的最大收益
                //t2表示这次是买入,买入后的最大收益
                //t3表示这次什么也不做的最大收益
                t1=t2=t3=-INF;
                if(rear1>front1) t1 = a[deque1[front1]] - (j-deque1[front1])*bp[i];
                if(rear2>front2) t2 = a[deque2[front2]] + (deque2[front2]-j)*sp[i];
                t3=dp[i-1][j];
                dp[i][j] = max3(t1,t2,t3);
    
                //更新单调队列deque1
                if(s[i]>0 && rear2>front2 && deque2[front2]==j+1) front2++;
                if(s[i]>0 && j+s[i]+1<=mp && a[j+s[i]+1] != -INF){
                    while(rear2>front2 && a[j+s[i]+1]+(j+s[i]+1-deque2[rear2-1])*sp[i] >= a[deque2[rear2-1]]) rear2--;
                    deque2[rear2++]=j+s[i]+1;
                }
                //更新另外一个单调队列deque2,表示这次交易要买入股票的情况
                if(b[i]>0 && rear1>front1 && deque1[front1]==j-b[i]) front1++;
                if(b[i]>0 && a[j] != -INF){
                    while(rear1>front1 && a[j]+(j-deque1[rear1-1])*bp[i] >= a[deque1[rear1-1]]) rear1--;
                    deque1[rear1++]=j;
                }
            }
    
            //update array a[]
            for(int j=0; j<=mp; j++)
                a[j]=dp[i-w+1][j];
        }
    }
    
    int main()
    {
        int T;
        cin>>T;
        while(T--){
            sf("%d %d %d",&t,&mp,&w);
            for(int i=1; i<=t; i++){
                sf("%d %d %d %d",&s[i],&b[i],&sp[i],&bp[i]);
            }
            //特判一下,只有0天或只能手持0股,吴神还能做什么呢?
            if(t==0 || mp==0){
                ans=0;
                cout<<ans<<endl;
                continue;
            }
            //第i天交易后,只能在第i+w+1天再交易,现在将w++,于是第i天后,第i+w天才能交易。
            w++;
            //if(w==0) w=1;
            ini();
            //dp[i][j]表示第i天,手持j股时最多能赚多少钱。
            DP();
            ans=-INF;
            //对第t天(最后一天)的结果,找最大值。that's it!!!
            for(int j=0; j<=mp; j++)
                if(dp[t][j]>ans)
                    ans=dp[t][j];
            if(ans<0) ans=0;
            pf("%d",ans);
            pfn
        }
        return 0;
    }
  • 相关阅读:
    虚方法、重写方法以及抽象类的知识小结
    DateTime时间格式
    JavaScript中Eval()函数的作用
    JQuery Event属性说明
    正则表达式30分钟入门教程
    dwz的form表单中url的变量替换
    dwz中权限的控制
    Dwz下拉菜单的二级联动
    Win7下用IIS发布网站
    IntelliJ IDEA 常用快捷键列表及技巧大全
  • 原文地址:https://www.cnblogs.com/Lattexiaoyu/p/2551355.html
Copyright © 2020-2023  润新知