• 股票买卖问题


    记录几道股票买卖问题,学学动态规划。

    力扣121:买卖股票的最佳时机

    题解:只能买卖一次获取最大利润,求数组最大上升差。用一个变量minn存储最低点,另一个变量maxx维护最大利润。

          public int maxProfit(int a[]) {
              int minn=Integer.MAX_VALUE, maxx=0, n=a.length;
              for(int i=0;i<n;i++) {
                  if(minn>a[i])
                      minn=a[i];
                  else 
                      maxx=Math.max(maxx, a[i]-minn);
              }
              return maxx;
          }
    力扣121

    力扣122:买卖股票的最佳时机II

    题解:相比上一题可以买卖无数次,即累计所有上升序列序列之差。

    class Solution {
        public int maxProfit(int[] a) {
            int len=a.length;
            int sum=0;
            for(int i=1;i<len;i++){
                if( a[i-1]<a[i] )
                    sum+=a[i]-a[i-1];
            }
            return sum;
        }
    }
    力扣122

    力扣714:买卖股票的最佳时机含手续费

    题解:含手续费则需要计算卖出是否值得,并且在最合适的时候卖出,可以交易无限次。用纯利润计算,买入(持有)则花钱(-a[i]),卖出(不持有)则赚钱(+a[i]-fee)。买与不买,卖与不卖,很容易看出是动态规划。

    dp[i][0]表示第i天不持有股票,昨天和今天的持有情况可能是:

    • 昨天不持有,今天也不持有。dp[i-1][0];
    • 昨天持有,今天不持有。dp[i-1][1]+a[i];
    • dp[i][0] = max(dp[i-1][0], dp[i-1][1]+a[i]-fee);

    dp[i][1]表示第i天持有股票,昨天和今天的持有情况可能是:

    • 昨天持有,今天持有。dp[i-1][1];
    • 昨天不持有,今天持有。dp[i-1][0]-a[i];
    • dp[i][1] = max(dp[i-1][1], dp[i-1][0]-a[i]);

    全部计算都是建立在昨天的状态上,可以滚掉一维。

    class Solution {
        public int maxProfit(int[] a, int fee) {
            int n=a.length;
            int[] dp=new int[2];//dp[0]表示不持有股票 dp[1]表示持有股票
            dp[0]=0;
            dp[1]=-a[0];
            for(int i=1;i<n;i++) {
                int temp=dp[0];
                dp[0]=Math.max(dp[0], dp[1]+a[i]-fee);//昨天不持有 或者 昨天持有,今天卖了
                dp[1]=Math.max(dp[1], temp-a[i]);//昨天持有   或者 昨天不持有,今天买了
            }
            return dp[0];
        }
    }
    力扣714

    力扣309:最佳买卖股票时机含冷冻期

    题解:卖出股票后有一天冷冻期,相比上一题,衍生出另一种状态不持有(冷冻期),重新分析。

    dp[0]表示不持有(非冷冻期),昨天和今天的情况可能是:

    • 昨天不持有(非冷冻期)dp[0];
    • 昨天不持有(冷冻期)dp[2];
    • dp[0] = max(dp[0], dp[2]);

    dp[1]表示持有,昨天和今天的情况可能是:

    • 昨天持有,今天继续持有;
    • 昨天不持有(非冷冻期),今天持有;
    • dp[1] = max(dp[1],dp[2]-a[i]);

    dp[2]表示不持有,必然是昨天持有,今天卖出:dp[2]=dp[1]+a[i];

    class Solution {
        public int maxProfit(int[] a) {
            int n=a.length,temp0,temp1,temp2;
            if(n==0)
                return 0;
            int[] dp=new int[3];//0表示不持有 1表示持有 2表示冷冻期(不持有)
            dp[0]=0;
            dp[1]=-a[0];
            dp[2]=0;
            for(int i=1;i<n;i++) {
                //不持有非冷冻期的情况:昨天不持有(非冷冻期) 昨天不持有(冷冻期)
                temp0=Math.max(dp[0], dp[2]);
                //昨天持有,今天持有  vs   昨天不持有(非冷冻期),今天持有
                temp1=Math.max(dp[1], dp[0]-a[i]);
                //冷冻期必然是昨天持有,今天刚卖出
                temp2=dp[1]+a[i];
                dp[0]=temp0;
                dp[1]=temp1;
                dp[2]=temp2;
            }
            return Math.max(dp[0], dp[2]);
        }
    }
    力扣309

    力扣123:买卖股票的最佳时机 III

    题解:只能买卖2次,重新分析状态

    • dp[0]表示啥也不干,不买不卖,永远为0;
    • dp[1]表示第一次买入的状态,可以从原本就买入(dp[1])或者 当天买入(dp[0]-a[i])获得该状态;
    • dp[2]表示第一次卖出的状态,可以从原本就卖出(dp[2])或者 当天卖出(dp[1]+a[i])获得该状态;
    • dp[3]表示第二次买入的状态,可以从原本就第二次买入(dp[3])或者 当天第二次买入(dp[2]-a[i])获得该状态;
    • dp[4]表示第二次卖出的状态,可以从原本就第二次卖出(dp[4])或者 当天第二次卖出(dp[3]+a[i])或者该状态;
    class Solution {
        public int maxProfit(int[] a) {
            int n=a.length;
            if(n==0)
                return 0;
            int[] dp=new int[5];
            //什么都不干(永远为0)、第一次买入、第一次卖出、第二次买入、第二次卖出
            dp[1]=dp[3]=-a[0];//dp[3]初始化表示舍弃掉一次机会,只买卖一次    
            dp[2]=dp[4]=0;    
            for(int i=1;i<n;i++) {
                dp[1]=Math.max(dp[1], dp[0]-a[i]);
                dp[2]=Math.max(dp[2], dp[1]+a[i]);
                dp[3]=Math.max(dp[3], dp[2]-a[i]);
                dp[4]=Math.max(dp[4], dp[3]+a[i]);
            }
            //return Math.max(dp[2], dp[4]);
            return dp[4];//覆盖了买卖两次和买卖一次的答案
        }
    }
    力扣123

    力扣124:买卖股票的最佳时机 IV

    题解:可以买卖k次股票,题目k巨大,买卖次数其实不超过天数的一半。

    这里有个概念要定义清楚,买入算不算交易一次,卖出算不算交易一次,买入后卖出算几次?

    dp[天数][持有状态,0表示不持有,1表示持有][卖出次数];

    • 今日不持有状态,可以从两种状态衍生出来:【昨天持有,今天卖出】或【昨天本来就不持有】;
    • dp[i][0][j] = Math.max( dp[i-1][1][j-1]+a[i], dp[i-1][0][j] );
    • 今日持有状态,可以从两种状态衍生出来:【昨天没有,今天买入】或【昨天本来就有】,注意买入是不加j的;
    • dp[i][1][j] = Math.max( dp[i-1][0][j]-a[i], dp[i-1][1][j] );

    取值当然是取不持有(卖出),maxx=Math.max(maxx, dp[i][0][j]);

    对于j=0,如果遍历到的当天还没有卖出过,那么选择最低买入价,dp[i][1][0]=Math.max(dp[i-1][1][0], dp[i-1][0][0]-a[i]);

    class Solution {
        public int maxProfit(int k, int[] a) {
            int n=a.length;
            if(n<2 || k==0)
                return 0;
            k=Math.min(n/2, k);
            //dp[天数][是否持有股票][卖出次数]
            int[][][] dp=new int[n][2][k+1];
            //初始化每个卖出次数,在持有(买入)状态下为绝对小
            for(int i=1;i<=k;i++) {
                dp[0][1][i]=-10000;
            }
            //然后从第一天开始去遍历,后续会比较  绝对小和真实买卖状态,从而覆盖掉绝对小
            //这个绝对小不可以是0,否则会覆盖掉真实的卖出状态例如-2之类的
            dp[0][1][0]=-a[0];
            int maxx=0;
            for(int i=1;i<n;i++) {
                //如果当天还没有卖出过,那么选择最低买入价
                dp[i][1][0]=Math.max(dp[i-1][1][0], dp[i-1][0][0]-a[i]);
                for(int j=1;j<=k;j++) {
                    //昨天持有,今天卖出,刚好j次   vs    原本就卖出j次的状态值
                    dp[i][0][j] = Math.max( dp[i-1][1][j-1]+a[i], dp[i-1][0][j]);
                    //前一天不持有,当天买入        vs    原本就卖出j次的买入状态
                    dp[i][1][j] = Math.max( dp[i-1][0][j]-a[i], dp[i-1][1][j] );
                    
                    maxx=Math.max(maxx, dp[i][0][j]);
                }
            }
            return maxx;
        }
    }
    力扣124
  • 相关阅读:
    猫 先吃药
    用抛物线筛选素数
    999999999分解质因数
    九宫数独简介(转)
    空间想象力大战!Smale球面外翻问题
    神奇的分形艺术(一):无限长的曲线可能围住一块有限的面积
    Kobon问题新进展:17条直线可构成多少个互不重叠的三角形
    关于2008:你必须知道的10个事实
    正多边形的滚动与旋轮线下的面积
    我见过的最酷的排序算法演示(乐死我了~)
  • 原文地址:https://www.cnblogs.com/shoulinniao/p/14204270.html
Copyright © 2020-2023  润新知