代码随想录day49|转行当股神|121. 买卖股票的最佳时机|122.买卖股票的最佳时机II|Golang

代码随想录day49

向前看

121. 买卖股票的最佳时机

思路1:贪心

思路2:动态规划

动态规划

动规五部曲分析如下:

1、确定dp数组以及下标的含义

        dp[i][0] 表示第i天 持有股票 所得的最多现金 ,这里可能有同学疑惑,本题中只能买卖一次,持有股票之后哪还有现金呢?

        其实一开始现金是0,那么加入第i天买入股票,现金就是-prices[i],这是一个负数。

        dp[i][1]表示第i天 不持有股票 所得的最多现金。

        注意这里说的是“持有”,“持有”不代表就是当天“买入”!也有可能是昨天就买入了,今天保持持有的状态

        很多同学把“持有”和“买入”没分区分清楚。

        在下面递推公式分析中,我会进一步讲解。

2、确定递推公式

如果第i天持有股票,即dp[i][0],那么可以由两个状态推出来

  • 第i-1天就持有股票,那么就保持现状,所得的现金就是昨天持有股票的所得现金,即dp[i-1][0]
  • 第i天买入股票,那么所得现金就是买入今天股票后所得现金,即 - prices[i], 那么dp[i][0]应该选所得现金最大的,所以dp[i][0] = max(dp[i-1][0], -prices[i])

 如果第i天不持有股票dp[i][1],也可以由两个状态推出来。

  • 第i-1天就不持有股票,那么就保持现状,所得现金就是昨天不持有股票的所得现金,即 dp[i-1][1]
  • 第i天卖出股票,所得现金就是按照今天股票价格卖出去所得现金,即dp[i-1][0]+prices[i]

        同样dp[i][1]取最大的,dp[i][1] = max(dp[i - 1][1], prices[i] + dp[i - 1][0]);  这样递归公式我们就分析完了

3、dp数组如何初始化

由递推公式:

        dp[i][0] = max(dp[i-1][0], -prices[i])

        dp[i][1] = max(dp[i-1][1], prices[i]+dp[i-1][0])

我们可以看出来,都是要从dp[0][0]和dp[0][1]推导出来。

        那么dp[0][0]表示第0天持有股票,此时的持有股票一定就是买入股票了,因为不可能有前一天推导出来。所以dp[0][0] =  -prices[0]

        dp[0][1]表示第0天不持有股票,那么不持有股票的现金就是0。所以dp[0][1] = 0

4、确定遍历顺序

        从递推公式可以看出dp[i]都是有dp[i-1]推导出来的,那么一定是从前向后遍历。

5、举例推导dp数组

        以示例1,输入[7,1,5,3,6,4]为例,dp数组状态如下:

         dp[0][0]:第0天持有股票的现金就是 -prices[0] = -7

         dp[0][1]:第0天不持有股票的现金就是 0

         dp[1][0]:第1天持有股票的现金就是 max(dp[1-1][0], -prices[1]) = -1

         dp[1][1]:第1天不持有股票的现金就是 max(dp[1-1][1], prices[1]+dp[1-1][0])   = 0

         dp[2][0]:第2天持有股票的现金就是 max(dp[2-1][0], -prices[2])  =  -1

         dp[2][1]:第2天不持有股票的现金就是 max(dp[2-1][1], prices[2]+dp[2-1][0]) = 5

以此类推......

        dp[5][1]就是最终结果。为什么不是dp[5][0]呢?

因为本题中不持有股票状态所得金钱一定比持有股票状态得到的多!

以上分析完毕,Go代码如下:

func maxProfit(prices []int) int {
    n := len(prices)
    dp := make([][]int, n)
    for i:=0;i<n;i++{
        dp[i] = make([]int,2)
    }
    dp[0][0] = -prices[0]
    dp[0][1] = 0
    for i:=1;i<n;i++{
        dp[i][0] = max(dp[i-1][0], -prices[i])
        dp[i][1] = max(dp[i-1][1], dp[i-1][0]+prices[i])
    }
    return dp[n-1][1]
}
func max(a, b int) int {
    if a > b {
        return a 
    }
    return b
}

122. 买卖股票的最佳时机 II

 思路

        本题和121. 买卖股票的最佳时机的唯一区别本题股票可以买卖多次了(注意只有一只股票,所以再次购买前要出售掉之前的股票)

        在动规五部曲中,这个区别主要是体现在递推公式上,其他都和121. 买卖股票的最佳时机一样一样的

所以我们重点讲一讲递推公式。

这里重申一下dp数组的含义:

dp[i][0]表示第i天持有股票的所得现金。

dp[i][1]表示第i天不持有股票的所得最多现金。

如果第i天持有股票即dp[i][0], 那么可以由两个状态推出来

  • 第i-1天就持有股票,那么就保持现状,所得现金就是昨天持有股票的所得现金 即:dp[i - 1][0]
  • 第i天买入股票,所得现金就是昨天不持有股票的所得现金减去 今天的股票价格 即:dp[i - 1][1] - prices[i]

        注意这里和121. 买卖股票的最佳时机唯一不同的地方,就是推导dp[i][0]的时候,第i天买入股票的情况

        在121. 买卖股票的最佳时机中,因为股票全程只能买卖一次,所以如果买入股票,那么第i天持有股票即dp[i][0]一定就是 -prices[i]。

        而本题,因为一只股票可以买卖多次,所以当第i天买入股票的时候,所持有的现金可能有之前买卖过的利润。

        那么第i天持有股票即dp[i][0],如果是第i天买入股票,所得现金就是昨天不持有股票的所得现金 减去 今天的股票价格 即:dp[i - 1][1] - prices[i]。

再来看看如果第i天不持有股票即dp[i][1]的情况, 依然可以由两个状态推出来

  • 第i-1天就不持有股票,那么就保持现状,所得现金就是昨天不持有股票的所得现金 即:dp[i - 1][1]
  • 第i天卖出股票,所得现金就是按照今天股票佳价格卖出后所得现金即:prices[i] + dp[i - 1][0]

注意这里和121. 买卖股票的最佳时机就是一样的逻辑,卖出股票收获利润(可能是负值)天经地义!

// 买卖股票的最佳时机Ⅱ 动态规划
// 时间复杂度:O(n) 空间复杂度:O(n)
func maxProfit(prices []int) int {
    dp := make([][]int, len(prices))
    for i := range dp {
        dp[i] = make([]int,2)
    }

    // dp[i][0] 表示第i天持有股票所得现金。
    // dp[i][1] 表示第i天不持有股票所得最多现金
    dp[0][0] = -prices[0]
    dp[0][1] = 0
    

    for i := 1; i < len(prices); i++ {
        // 如果第i天持有股票即dp[i][0], 那么可以由两个状态推出来
        // 第i-1天就持有股票,那么就保持现状,所得现金就是昨天持有股票的所得现金 即:dp[i - 1][0]
        // 第i天买入股票,所得现金就是昨天不持有股票的所得现金减去 今天的股票价格 即:dp[i - 1][1] - prices[i]
        dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]) // 注意这里是和121. 买卖股票的最佳时机唯一不同的地方。

        // 在来看看如果第i天不持有股票即dp[i][1]的情况, 依然可以由两个状态推出来
        // 第i-1天就不持有股票,那么就保持现状,所得现金就是昨天不持有股票的所得现金 即:dp[i - 1][1]
        // 第i天卖出股票,所得现金就是按照今天股票佳价格卖出后所得现金即:prices[i] + dp[i - 1][0]
        dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i])
    }
    
  // 最终手里不持有股票才能保证收益最大化
    return dp[len(prices) - 1][1]
}

func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}


大家可以本题和121. 买卖股票的最佳时机的代码几乎一样,唯一的区别在:

dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]);

        这正是因为本题的股票可以买卖多次! 所以买入股票的时候,可能会有之前买卖的利润即:dp[i - 1][1],所以dp[i - 1][1] - prices[i]。

        想到到这一点,对这两道题理解的比较深刻了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值