我的LeetCode代码仓:https://github.com/617076674/LeetCode
原题链接:https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/description/
题目描述:
知识点:动态规划
枚举第一笔交易卖出的时间firstDealSell,对[0, firstDealSell]范围内的价格求解LeetCode121——买卖股票的最佳时机中的问题,得到结果result1,再对[firstDealSell, n - 1]范围内的价格再一次求解LeetCode121——买卖股票的最佳时机中的问题,其中n为prices数组的大小,得到结果result2,求result1 + result2的最大值。
时间复杂度是O(n ^ 2)。空间复杂度是O(1)。
当然,在此基础之上,结合LeetCode122——买卖股票的最佳时机II,我们可以做一些小小的优化。
对于第一次和第二次卖出的时间点,firstDealSell和secondDealSell,其之前的价格一定是一个上坡,其之后的价格一定是一个下坡,我们在价格坡顶卖出。
JAVA代码:
public class Solution {
public int maxProfit(int[] prices) {
int result = 0;
if(prices.length == 0){
return result;
}
int firstDealSell; //第一笔交易在firstDealSell卖出
int secondDealSell; //第二笔交易在secondDealSell卖出
for(secondDealSell = prices.length - 1; secondDealSell > 0; secondDealSell--){
if(prices[secondDealSell - 1] < prices[secondDealSell]){
break;
}
}
for(firstDealSell = 1; firstDealSell < prices.length; firstDealSell++){
while(firstDealSell + 1 < prices.length && prices[firstDealSell + 1] >= prices[firstDealSell]){
firstDealSell++;
}
int result1 = maxProfit(prices, 0, firstDealSell);
int result2 = maxProfit(prices, firstDealSell + 1, secondDealSell);
if(result1 + result2 > result){
result = result1 + result2;
}
}
return result;
}
private int maxProfit(int[] prices, int left, int right){
int result = 0;
if(right - left < 1){
return result;
}
int minPrice = prices[left];
for(int i = left + 1; i <= right; i++){
result = Math.max(result, prices[i] - minPrice);
minPrice = Math.min(minPrice, prices[i]);
}
return result;
}
}
LeetCode解题报告:
本思路及下述改进思路均参考自:https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/discuss/135704/Detail-explanation-of-DP-solution。
状态定义: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)。
JAVA代码:
public 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++){
for(int b = 1; b < i; 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];
}
}
LeetCode解题报告:
在思路二中,我们对第一笔交易和第二笔交易进行了分别计算,因为第1笔交易的前一笔交易不存在,会产生数组越界问题。
我们可以在第一笔交易前增加一笔虚拟的第0笔交易,其f(0, y)均为0,这样就避免了对第一笔交易和第二笔交易的讨论。
同时,我们会发现思路二中min的计算其实重复进行了,我们完全可以忽略内层的循环变量b的循环。
时间复杂度和空间复杂度均是O(kn),其中k为交易次数,n为prices数组的大小。
JAVA代码:
public class Solution {
public int maxProfit(int[] prices) {
int result = 0;
if (0 == prices.length) {
return result;
}
int[][] dp = new int[3][prices.length];
for(int k = 1; k <= 2; k++){
dp[k][0] = 0;
int min = prices[0];
for(int i = 1; i < prices.length; i++){
dp[k][i] = Math.max(dp[k][i - 1], prices[i] - min);
min = Math.min(min, prices[i] - dp[k - 1][i - 1]);
}
}
return dp[2][prices.length - 1];
}
}
LeetCode解题报告:
交换内外层循环的位置。
时间复杂度和空间复杂度均是O(kn),其中k为交易次数,n为prices数组的大小。
JAVA代码:
public class Solution {
public int maxProfit(int[] prices) {
int result = 0;
if (0 == prices.length) {
return result;
}
int[][] dp = new int[3][prices.length];
int[] min = new int[3];
for(int i = 1; i < 3; i++){
min[i] = prices[0];
}
for(int i = 1; i < prices.length; i++){
for(int k = 1; k <= 2; k++){
dp[k][i] = Math.max(dp[k][i - 1], prices[i] - min[k]);
min[k] = Math.min(min[k], prices[i] - dp[k - 1][i - 1]);
}
}
return dp[2][prices.length - 1];
}
}
LeetCode解题报告:
从思路四中我们发现第i列的变量只依赖于第i - 1列的变量,压缩该维度。
时间复杂度是O(kn),其中k为交易次数,n为prices数组的大小。空间复杂度是O(k)。
JAVA代码:
public class Solution {
public int maxProfit(int[] prices) {
int result = 0;
if (0 == prices.length) {
return result;
}
int[] dp = new int[3];
int[] min = new int[3];
for(int i = 1; i < 3; i++){
min[i] = prices[0];
}
for(int i = 1; i < prices.length; i++){
for(int k = 1; k <= 2; k++){
dp[k] = Math.max(dp[k], prices[i] - min[k]);
min[k] = Math.min(min[k], prices[i] - dp[k - 1]);
}
}
return dp[2];
}
}
LeetCode解题报告:
针对本题,只进行2次交易,优化代码。
时间复杂度是O(n),n为prices数组的大小。空间复杂度是O(1)。
JAVA代码:
public class Solution {
public int maxProfit(int[] prices) {
int buy1 = Integer.MAX_VALUE;
int sell1 = 0;
int buy2 = Integer.MAX_VALUE;
int sell2 = 0;
for(int i = 0; i < prices.length; i++){
sell1 = Math.max(sell1, prices[i] - buy1);
buy1 = Math.min(buy1, prices[i]);
sell2 = Math.max(sell2, prices[i] - buy2);
buy2 = Math.min(buy2, prices[i] - sell1);
}
return sell2;
}
}
LeetCode解题报告: