携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第13天,点击查看活动详情
题目
给定一个数组,它的第 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 <= 10^50 <= prices[i] <= 10^5
题解
思路
最多可完成两笔交易意味着总共有三种情况:买卖一次,买卖两次,不买卖。
具体到每一天结束总共有 5 种状态:
- 未进行买卖状态;
- 第一次买入状态;
- 第一次卖出状态;
- 第二次买入状态;
- 第二次卖出状态。
所以我们可以定义状态 dp[i][j] ,表示为:第 i 天第 j 种情况(0 <= j <= 4)下,所获取的最大利润。
注意:这里第第 j 种情况,并不一定是这一天一定要买入或卖出,而是这一天所处于的买入卖出状态。比如说前一天是第一次买入,第二天没有操作,则第二天就沿用前一天的第一次买入状态。
接下来确定状态转移公式:
- 第 0 种状态下显然利润为 0,可以直接赋值为昨天获取的最大利润,即
dp[i][0] = dp[i - 1][0]。 - 第 1 种状态下可以有两种状态推出,取最大的那一种赋值:
- 不做任何操作,直接沿用前一天买入状态所得的最大利润:
dp[i][1] = dp[i - 1][1]。 - 第一次买入:
dp[i][1] = dp[i - 1][0] - prices[i]。
- 不做任何操作,直接沿用前一天买入状态所得的最大利润:
- 第 2 种状态下可以有两种状态推出,取最大的那一种赋值:
- 不做任何操作,直接沿用前一天卖出状态所得的最大利润:
dp[i][2] = dp[i - 1][2]。 - 第一次卖出:
dp[i][2] = dp[i - 1][1] + prices[i]。
- 不做任何操作,直接沿用前一天卖出状态所得的最大利润:
- 第 3 种状态下可以有两种状态推出,取最大的那一种赋值:
- 不做任何操作,直接沿用前一天买入状态所得的最大利润:
dp[i][3] = dp[i - 1][3]。 - 第二次买入:
dp[i][3] = dp[i - 1][2] - prices[i]。
- 不做任何操作,直接沿用前一天买入状态所得的最大利润:
- 第 4 种状态下可以有两种状态推出,取最大的那一种赋值:
- 不做任何操作,直接沿用前一天卖出状态所得的最大利润:
dp[i][4] = dp[i - 1][4]。 - 第二次卖出:
dp[i][4] = dp[i - 1][3] + prices[i]。
- 不做任何操作,直接沿用前一天卖出状态所得的最大利润:
下面确定初始化的边界值:
可以很明显看出第一天不做任何操作就是 dp[0][0] = 0,第一次买入就是 dp[0][1] = -prices[i]。
第一次卖出的话,可以视作为没有盈利(当天买卖,价格没有变化),即 dp[0][2] = 0。第二次买入的话,就是 dp[0][3] = -prices[i]。同理第二次卖出就是 dp[0][4] = 0。
在递推结束后,最大利润肯定是无操作、第一次卖出、第二次卖出这三种情况里边的最大值。
因为我们维护的是最大值,则第一次卖出、第二次卖出所获得的利润肯定大于等于 0。而且,如果最优情况为一笔交易,那么在转移状态时,我们允许在一天内进行两次交易,则一笔交易的状态可以转移到两笔交易完成状态。所以最终答案为 dp[size - 1][4],size 为股票天数。
代码
结语
业精于勤,荒于嬉;行成于思,毁于随。