[LeetCode] 123. Best Time to Buy and Sell Stock III

原题链接:https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/

Buy and Sell Stock专题相关题目

121. Best Time to Buy and Sell Stock
122. Best Time to Buy and Sell Stock II
123. Best Time to Buy and Sell Stock III
188. Best Time to Buy and Sell Stock IV
309. Best Time to Buy and Sell Stock with Cooldown
714. Best Time to Buy and Sell Stock with Transaction Fee

1. 题目简介

Say you have an array for which the i th element is the price of a given stock on day i.

Design an algorithm to find the maximum profit. You may complete at most two transactions.

Note: You may not engage in multiple transactions at the same time (i.e., you must sell the stock before you buy again).

给出一列数组,第i个元素代表着第i天某支股票的价格。最多可以交易2次,也就是最多买2次,卖2次,而且必须先买后卖。

Example 1:

Input: [3,3,5,0,0,3,1,4]
Output: 6
Explanation: 
Buy on day 4 (price = 0) and sell on day 6 (price = 3), 
profit = 3-0 = 3.
Then buy on day 7 (price = 1) and sell on day 8 (price = 4), 
profit = 4-1 = 3.
Example 2:

Input: [1,2,3,4,5]
Output: 4
Explanation: 
Buy on day 1 (price = 1) and sell on day 5 (price = 5), profit = 5-1 = 4.

Note that you cannot buy on day 1, buy on day 2 and sell them later, 
as you are engaging multiple transactions at the same time. 
You must sell before buying again.
Example 3:

Input: [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is done, i.e. max profit = 0.

2. 解题思路

动态规划法

注:这部分内容参考了 https://blog.csdn.net/linhuanmars/article/details/23236995 的解题思路。

步骤1: 设计数组

最多可以进行2次交易,我们把这个次数抽象化,变成最多可以进行k次交易,最后的答案只需要把k取2即可。
对于最多进行k次交易的情况,可以采用“局部最优+全局最优”的方法,需要设计2个二维数组,一个是全局最优,一个是局部最优。

全局最优 :global [ i ][ j ],存放当前到达第 i 天时,最多进行 j 次交易,所获得的最大利润,并且第 j 次交易不是在当天(第 i 天)卖出的。

局部最优 :local [ i ][ j ],存放当前到达第 i 天时,最多进行 j 次交易,规定其中最后一次交易必须是当天(第 i 天)卖出的,所获得的最大利润。

步骤2:寻找状态转移方程

1 . 全局最优的状态转移方程:

global[i][j] = max( local[i][j] , global[i-1][j] )

在 j 次交易下,前一天的全局最优今天的局部最优,哪个更大,哪个就是 今天的全局最优

2 . 局部最优的状态转移方程:

local[i][j] = max(global[i-1][j-1]+max(diff,0) , local[i-1][j]+diff)
其中diff = prices[i]-prices[i-1]

global[ i-1 ][ j-1 ]+ max(diff,0) 的意思是,是在前面 j-1 次交易的基础上,最后一次交易(第 j 次)在倒数第二天(第 i-1 天)买进,在最后一天(第 i 天)卖出 所获得的利润。
这里特别需要强调的是,global[ i-1 ][ j-1 ]意味着第i-1天没有交易,因此不会出现在第 i-1 天同时买卖的矛盾。

local[ i-1 ][ j ] + diff 的意思是,在前面 j 次交易的基础上,把最后一次交易从第 i-1 天卖出改成第 i 天卖出。由于local[ i-1 ][ j ]意味着最后一次交易(第 j 次交易)一定在 i-1 天卖出,如果改成在第 i 天卖出,会把第 i 天卖的合并到之前 i-1 卖出的那次交易中,因此并不会增加交易次数,所以是 local[ i-1 ][ j ] + diff

步骤3:举例说明

以[3,3,5,0,0,3,1,4]为例。按照上述方法进行递推,结束后的local数组和global数组为:
在这里插入图片描述

实现代码

class Solution {
   public int maxProfit(int[] prices) {
		int length = prices.length;
	    if(prices==null || length==0)
	        return 0;
	    
	    
	    int[][] local = new int[length][3];
	    int[][] global = new int[length][3];
	    
	    for(int i = 1; i<length ; i++)
	    {
	        int diff = prices[i]-prices[i-1];
	        for(int j=1;j<=2;j++)
	        {
	            local[i][j] = Math.max(global[i-1][j-1]+(diff>0?diff:0) , local[i-1][j]+diff);
	            global[i][j] = Math.max( local[i][j] , global[i-1][j] );
	           
	        }
	    }  
	    
	    return global[length-1][2];
	}

}

步骤4:进一步优化

二维数组转化为一维数组,节省空间。
从下图可以看出,更新global数组时,我们每次想要更新global[ i ][ j ](下图黄色的部分),只会用到 local[i][j] 和 global[i-1][j](下图蓝色的部分)
在这里插入图片描述
更新local数组时,我们每次想要更新local[ i ][ j ](下图黄色的部分),只会用到 global[i-1][j-1] 和 local[i-1][j](下图蓝色的部分)

在这里插入图片描述

计算prices[ i ]时,上一行的数值在计算prices[ i-1 ]时已经算好了。那我们就直接用起来,用完不用再保存了。

所以,我们就能把二维数组压缩为一维数组,只需要每次 从后往前 更新global 和 local 的值。状态转移方程也就成了:

local[j] = Max(global[j-1]+ Max(diff,0) , local[j]+diff);
global[j] = Max( local[j] , global[j] );
	       

实现代码

class Solution {
  public int maxProfit(int[] prices) {
		int length = prices.length;
	    if(prices==null || length==0)
	        return 0;
	    
	    
	    int[] local = new int[3];
	    int[] global = new int[3];
	    
	    for(int i = 1; i<length ; i++)
	    {
	        int diff = prices[i]-prices[i-1];

	        for(int j=2;j>=1;j--)
	        {
	        	local[j] = Math.max(global[j-1]+(diff>0?diff:0) , local[j]+diff);
	            global[j] = Math.max( local[j] , global[j] );
	        }
	    }
	    return global[2];
	}
}

3. 参考资料

https://blog.csdn.net/linhuanmars/article/details/23236995

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值