买股票的最佳时机
描述
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
注意: 你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
样例
输入: [3,3,5,0,0,3,1,4]
输出: 6
解释: 在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。
输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
输入: [7,6,4,3,1]
输出: 0
解释: 在这个情况下, 没有交易完成, 所以最大利润为 0。
难度
高,动态规划
解法
状态定义:f(x, y) -------- 第x笔交易在第y天能取得的最大利润
状态转移:
(1)当x == 1时 第一笔交易进行时
a:我们可以选择在第y天不卖出也不买入,
a-1:如果此时y == 0,即第0天,那么f(1, 0) = 0,即取得的最大利润为0。
a-2:如果此时y > 0,那么此时我们的第x笔交易在第y天能取得的最大利润是f(1, y - 1),因为我们在第y天既不买入也不卖出,取得的最大利润自然和第x笔交易在第y - 1天能取得的最大利润相同。
b:我们可以选择在第y天卖出,
b-1:如果此时y == 0,显然,我们不可能在第0天卖出,这种情况不予讨论。
b-2:如果此时y > 0,f(x, y) = max(prices[y] - prices[b]),其中0 <= b < y,代表我们在第b天买入。
综上,对于x == 1的情况,当y == 0时,f(1, 0) = 0;当y > 0时,f(1, y) = max(f(1, y - 1), prices[y] - prices[b]),0 <= b < y。
(2)当x == 2时
a:我们可以选择在第y天不卖出也不买入,
a-1:如果此时y == 0,即第0天,那么f(x, y) = 0,即取得的最大利润为0。
a-2:如果此时y > 0,那么此时我们的第x笔交易在第y天能取得的最大利润是f(2, y - 1),因为我们在第y天既不买入也不卖出,取得的最大利润自然和第x笔交易在第y - 1天能取得的最大利润相同。
b:我们可以选择在第y天卖出,
b-1:如果此时y == 0,显然,我们不可能在第0天卖出,这种情况不予讨论。
b-2:如果此时y > 0,f(x, y) = max(prices[y] - prices[b] , f(1, b - 1)),其中0 <= b < y,代表我们在第b天买入。
综上,对于x == 2的情况,当y == 0时,f(2, 0) = 0;当y > 0时,f(2, y) = max(f(2, y - 1), prices[y] - prices[b] + f(1, b - 1)),0 <= b < y。
时间复杂度是O(kn ^ 2),其中k为交易次数,n为prices数组的大小。空间复杂度是O(kn)。
例如:3,3,5,0,0,3,1,4
p | 3 | 3 | 5 | 0 | 0 | 3 | 1 | 4 |
1 | 0 | 0 | 2 | 2 | 2 | 3 | 3 | 4 |
2 | 0 | 0 | 2 | 2 | 2 | 5 | 5 | 6 |
代码
class Solution {
public int maxProfit(int[] prices) {
int result = 0;
if(0 == prices.length){
return result;
}
int[][] dp = new int[2][prices.length];
for(int k = 0; k < 2; k++){ //循环两次交易
dp[k][0] = 0;
int min = prices[0];
for(int i = 1; i < prices.length; i++){ //i代表正进行第i笔交易
for(int b = 1; b < i; b++){ //b代表买入时间
if(k == 0){ //第一次交易时
min = Math.min(min, prices[b]);
}else{ //第二次交易时
min = Math.min(min, prices[b] - dp[k - 1][b - 1]);
}
}
dp[k][i] = Math.max(dp[k][i - 1], prices[i] - min); //比较不卖出和卖出的利润
}
}
return dp[1][prices.length - 1];
}
}