刷题使我快乐,满脸开心.jpg

  • 来源:力扣(LeetCode)
  • 链接:https://leetcode.cn/problems/best-time-to-buy-and-sell-stock-iii/
  • 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题目

给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。

设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。

注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

示例 1:

输入:prices = [3,3,5,0,0,3,1,4]
输出:6
解释:在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。
     随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。

示例 2:

输入:prices = [1,2,3,4,5]
输出:4
解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。   
     注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。   
     因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。

示例 3:

输入:prices = [7,6,4,3,1] 
输出:0 
解释:在这个情况下, 没有交易完成, 所以最大利润为 0。

示例 4:

输入:prices = [1]
输出:0

提示:

  • 1 <= prices.length <= 105
  • 0 <= prices[i] <= 105

思路

这道题确实有些难度,不过如果做过买卖股票的最佳时机 II,应该会有所启发,同样使用动态规划来解决问题

动态规划

使用动态规划的话,那其实就是需要看怎么全拆解问题

dp定义

这次的交易变为了两次,那么我们的定义就不能再是买入和卖出两个情况了,而是四种:

  • 买一次
  • 买一次、卖一次
  • 买两次、卖一次
  • 买两次、卖两次

所以我们的dp数组定义应该如下:

buy1sell0[i] 为第i天时,买一次的最大收益
buy1sell1[i] 为第i天时,买一次、卖一次的最大收益
buy2sell1[i] 为第i天时,买两次、卖一次的最大收益
buy2sell2[i] 为第i天时,买两次、卖两次的最大收益

状态方程
  • 买一次的状态下,要么最大值是保持不操作的昨天的金额,要么是今天买一次后的价格的负数,也就是 -price[i]

选用负数来操作,是因为我们的dp数组的定义是面向收益的最大值,而买入价格对于收益的影响是负的,所以要是用负数来表示

buy1sell0[i] = max(buy1sell0[i-1], -price[i])

  • 买一次、卖一次状态下,要么是昨天的金额,要么是在买一次的最大值基础上,按今天价格出售,因为允许当日同时买入又卖出,所以不用前一天的买一次数据,即buy1sell0[i]+price[i]

buy1sell1[i] = max(buy1sell1[i-1], buy1sell0[i]+price[i])

  • 买两次、卖一次状态下,有点类似买一次的状态,只是我们是在买一次、卖一次最大值的基础上操作

buy2sell1[i] = max(buy2sell1[i-1], buy1sell1[i]-price[i])

  • 买两次、卖两次状态下,有点类似买一次、卖一次的状态,只是我们是在买两次、卖一次最大值的基础上操作

buy2sell2[i] = max(buy2sell2[i-1], buy2sell1[i]+price[i])

此时,我们依然能够发现,其实所有的状态都只跟之前一天的数据有关系,所以只保留之前一天的数据即可

  • 初始化
    买一次、和买两次、卖一次 两个状态会有些特殊,因为他们是买入操作,此时很有可能会是一个负数,所以不能保持初始化为0,所以需要初始化为第一天价格的负数

基本上思路就是如此了,直接上代码,

代码

func maxProfit(prices []int) int {
	// 动态规划 初始化
	buy1sell0, buy1sell1, buy2sell1, buy2sell2 := -prices[0], 0, -prices[0], 0
	for _, price := range prices {
        // 四个状态转移方程
    	// 可以简化为只是用一个变量保存,且不需要使用临时变量中转
    	// 	买一次 buy1sell0[i] = max(buy1sell0[i-1], -price[i])
		buy1sell0 = max(buy1sell0, -price)
	    //	出一次 buy1sell1[i] = max(buy1sell1[i-1], buy1sell0[i]+price[i])
		buy1sell1 = max(buy1sell1, buy1sell0+price)
	    //	买两次 buy2sell1[i] = max(buy2sell1[i-1], buy1sell1[i]-price[i])
		buy2sell1 = max(buy2sell1, buy1sell1-price)
	    //	出两次 buy2sell2[i] = max(buy2sell2[i-1], buy2sell1[i]+price[i])
		buy2sell2 = max(buy2sell2, buy2sell1+price)
	}
    // 原本应该是 max(max(buy1sell0, buy1sell1), max(buy2sell1, buy2sell2))
    // 但是其实 buy1sell0、buy2sell1 肯定是小的,所以排除掉
    // 并且其实 buy1sell1 肯定是不大于buy2sell2的,所以也可以排除掉
	return buy2sell2
}