标签:span 递推 max list 方便 最大值 length 选择 self
该题相对之前两道题难了不少,做了两遍还是有点不熟。给定一个股票价格的数组,让你最多只能交易两次,求能够获得的最大利润。
这里有一个关键点:在某一天买入股票,并在当天卖出股票是合法的(支持t+0交易,不得不吐槽天朝t+1交易机制)。
举个例子[1,3,8],我们可以选择在第0天买入,然后第2天卖出。也可以在第0天买入,第1天卖出,然后又在第1天买入,最后再在第2天卖出。两种交易策略的利润是相同的。下面给出解决方案。
1.分治
这种办法相对简单一些,时间复杂度为o($n^2$)。做法很简单,就是遍历数组,当坐标为 i 时,分别求得prices[0-i]和prices[i-end]单次交易的最大利润,取两者和的最大值返回。这里就不给出代码了
2.动态规划
我们可以定义dp[i][j]为第i次交易,在第j天获得的最大利润(可以是在第j天卖出获得的利润,或者第j天之前的交易所获利润)。我们可以很简单获得如下递推公式:
$ dp[i][j] = max \left\{ \begin{aligned} & dp[i][j-1] \\ & dp[i-1][m] + prices[j] - prices[m], m\in [0,1,...,j-1] \end{aligned} \right. $
我们来解释这个公式,在第i轮交易过程中,如果第j天没有发生交易,那么到第j天为止,所获利润就是前一天的利润dp[i][j-1]。
另外,我们还需要做一轮遍历操作,获得这轮遍历的最大利润值。dp[i-1][m]也就是上一轮交易过程中,在第m天所获得的最大利润。这里包括两种情况:1. 第i-1轮交易没有在第m天卖出 2.第i-1轮交易在第m天卖出。其实两者都可以用上述代码解决。如果是第一种情况,那么这种情况很好理解,也就是我们遍历买入时间$$m\in [0,1,...,j-1]$$,并在第j天卖出所获得最大利润。对于第二种情况,也就是说在第m天卖出得到利润dp[i-1][m],然后我们再买入,相当于当天卖出,当天买入,然后在第j天卖出能够获得的利润。这种做法的时间复杂度也为o($n^2$)。这里给出代码:
1 class Solution: 2 def maxProfit(self, prices: List[int]) -> int: 3 if not prices: 4 return 0 5 length = len(prices) 6 if length == 1: 7 return 0 8 dp = [[0] * length for i in range(3)] 9 for i in range(1,3): 10 #这里dp[i][j]指的是在第j天完成第i次交易 11 for j in range(0, length): 12 for m in range(0, j): 13 dp[i][j] = max(dp[i-1][m]+prices[j]-prices[m], dp[i][j]) 14 dp[i][j] = max(dp[i][j-1], dp[i][j]) 15 return dp[-1][-1]
其实,如果我们画个图观察计算路径,有些计算是重复的。举个例子,在第1轮交易过程中,我们计算dp[1][3]时,需要遍历:
$dp[1][3] = dp[0][0]+prices[3]-prices[0] \\dp[1][3] = dp[0][1]+prices[3]-prices[1] \\ dp[1][3] = dp[0][2]+prices[3]-prices[2]$
找到上述3个式子中的最大利润值,其中prices[3]是一个定值。
计算dp[1][4]时,需要遍历:
$dp[1][4] = dp[0][0]+prices[4]-prices[0] \\dp[1][4] = dp[0][1]+prices[4]-prices[1] \\ dp[1][4] = dp[0][2]+prices[4]-prices[2] \\ dp[1][4] = dp[0][3]+prices[4]-prices[3]$
找到上述4个式子中的最大利润值,其中prices[4]是一个定值。
细心的童鞋可能已经发现了,在这个过程中,我们只需要最大化dp[i-1][m] - prices[m],将这个值保存下来,可以节省在第i轮交易过程中一些不必要的重复计算过程。
此时时间复杂度为o($n^2$), 代码如下:
1 class Solution: 2 def maxProfit(self, prices: List[int]) -> int: 3 #动态规划解决这个问题,这道题两刷还是不会 4 #主要是求出第一次交易和第二次交易后获得的最大利润 5 #为了方便,第二次交易后得到的利润与第一次交易后得到的利润有关,而第一次交易后得到的利润与第0次交易得到的利润有关 6 if not prices: 7 return 0 8 length = len(prices) 9 if length == 1: 10 return 0 11 dp = [[0] * length for i in range(3)] 12 for i in range(1,3): 13 #在第j日买入时所获利润为temp_max 14 #这里dp[i][j]指的是在第j天完成第i次交易,[如果在第j天买入并卖出,收益为0] 15 temp_max = dp[i-1][0] - prices[0] 16 for j in range(0, length): 17 temp_max = max(temp_max, dp[i-1][j] - prices[j]) 18 dp[i][j] = max(dp[i][j-1], temp_max+prices[j]) 19 return dp[-1][-1]
Best Time to Buy and Sell Stock III
标签:span 递推 max list 方便 最大值 length 选择 self
原文地址:https://www.cnblogs.com/xiaoyunjun/p/10995049.html