LeetCode-309. 最佳买卖股票时机含冷冻期

LeetCode-309. 最佳买卖股票时机含冷冻期

-.- 这题不会做,记录下大佬的题解(难度:中等)

一、题目内容

给定一个整数数组,其中第 i 个元素代表了第 i 天的股票价格 。​
设计一个算法计算出最大利润。在满足以下约束条件下,你可以尽可能地完成更多的交易(多次买卖一支股票):
你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
卖出股票后,你无法在第二天买入股票 (即冷冻期为 1 天)。
示例:

输入: [1,2,3,0,2]
输出: 3
解释: 对应的交易状态为: [买入, 卖出, 冷冻期, 买入, 卖出]

二、题解过程

1、问题分析

求最值问题最先想到的就是动态规划问题。动态规划的核心问题就是穷举。因为要求最值,肯定要把所有可行的答案穷举出来,然后求最值。但是动态规划有点特别,因为这类问题存在[重叠子问题],如果暴力穷举的话,效率会及其底下,所以需要[备忘录]或者[DP table]来优化穷举过程,避免不必要的计算。

2、转态定义

我们可以考虑用dp[i]来表示第 i 天结束后的最大收益,根据题意我们当天共有三种状态,持有,卖出冻结,卖出未冻结,此状态影响状态转移,因此我们会有三种不同的状态:
1)持有股票,对应的最大累计收益为dp[i][0];
2)卖出股票,处于冷冻期,手头为空,第二天不可交易,对应的最大累计收益为dp[i][1];
3)卖出股票,不处于冷冻期,第二天可交易,对应的最大累计收益为dp[i][2];

3、状态转移

如何进行转移呢?在第 i 天的时候,我们可以在不违反规则的情况下,进行买入或者卖出的操作,此时第 i 天的转态是由第 i-1 天的状态转移的;我们也可以不进行任何操作,则第 i 天的状态就是第 i-1 天的状态,我们分别对这三种状态进行分析:
1)对于dp[i][0],我们目前持有一支股票,可以假设第 i-1 天已经持有,则对应的状态为dp[i-1][0];也可以假设第 i 天买入这支股票,则对应的状态为dp[i-1][2] 加上买入这支股票的带来的负收益price[i]。因此状态转移方程为:
dp[i][0] = max(dp[i-1][0],dp[i-1][2] - price[i])
2) 对于dp[i][1],我们在第i天结束之后处于冷冻期的原因是在当天卖出了股票,因此在 i-1天必须持有一支股票,对应的状态为dp[i-1][0],加上当天卖出股票的正向收益price[i-1]。因此状态转移方程为:
dp[i][1] = dp[i-1][0] + price[i]
3) 对于dp[i][2],当前不持有股票,并且不可交易,则在第 i-1 天时不持有任何股票,如果处于冷冻期,则对应的状态为dp[i-1][1],如果不处于冷冻期,则对应的状态为dp[i-1][2]。因此状态转移方程为:
dp[i][2] = max(dp[i-1][1],dp[i-1][2])

注意到如果在最后一天 (第 i -1天) 结束之后,手上仍然持有股票,那么显然没有任何意义。因此更加精确的,最终的答案实际上是:
max(dp[i-1][1],dp[i-1][2])

我们可以选择将第1天的情况作为动态规划中的边界条件:
dp[0][0] = -price[0]
dp[0][1] = 0
dp[0][2] = 0

4、解法一代码:

时间复杂度是 O(n^2),n 为待匹配字符串的长度。

// A code block
Class Solution{
	public int maxProfit(int[] prices){
		if(prices.length == 0){
			return 0;
		}
		int n = prices.length;
		int[][] dp = new int[n][3];
		dp[0][0] = -prices[0];
		for(int i=1;i<n;i++){
			dp[i][0] = Math.max(dp[i-1][0],dp[i-1][2] - prices[i]);
			dp[i][1] = dp[i-1][0] + prices[i];
			dp[i][2] = Math.max(dp[i-1][1],dp[i-1][2]);
		}
		return Math.max(dp[n-1][1],dp[n-1][2]);
	}
}

5、优化方法

注意到上面的状态转移方程中,dp[i]只有dp[i-1]有关,而与之前所有的状态都无关,因此不必计算这些状态的存储态。

Class Solution{
	public int maxProfit(int[] prices){
		if(prices.length == 0){
			return 0;
		}
		int n = prices.length;
        int f0 = -prices[0];
        int f1 = 0;
        int f2 = 0;
        for (int i = 1; i < n; ++i) {
            int newf0 = Math.max(f0, f2 - prices[i]);
            int newf1 = f0 + prices[i];
            int newf2 = Math.max(f1, f2);
            f0 = newf0;
            f1 = newf1;
            f2 = newf2;
        }
        return Math.max(f1, f2);
	}
}

参考如下:

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/solution/zui-jia-mai-mai-gu-piao-shi-ji-han-leng-dong-qi-4/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

### 回答1: 好的,我来用中文回复这个链接:https://leetcode-cn.com/tag/dynamic-programming/ 这个链接是 LeetCode 上关于动态规划的题目集合。动态规划是一种常用的算法思想,可以用来解决很多实际问题,比如最长公共子序列、背包问题、最短路径等等。在 LeetCode 上,动态规划也是一个非常重要的题型,很多题目都需要用到动态规划的思想来解决。 这个链接里包了很多关于动态规划的题目,按照难度从简单到困难排列。每个题目都有详细的题目描述、输入输出样例、题目解析和代码实现等内容,非常适合想要学习动态规划算法的人来练习和提高自己的能力。 总之,这个链接是一个非常好的学习动态规划算法的资源,建议大家多多利用。 ### 回答2: 动态规划是一种算法思想,通常用于优化具有重叠子问题和最优子结构性质的问题。由于其成熟的数学理论和强大的实用效果,动态规划在计算机科学、数学、经济学、管理学等领域均有重要应用。 在计算机科学领域,动态规划常用于解决最优化问题,如背包问题、图像处理、语音识别、自然语言处理等。同时,在计算机网络和分布式系统中,动态规划也广泛应用于各种优化算法中,如链路优化、路由算法、网络流量控制等。 对于算法领域的程序员而言,动态规划是一种必要的技能和知识点。在LeetCode这样的程序员平台上,题目分类和标签设置十分细致和方便,方便程序员查找并深入学习不同类型的算法LeetCode动态规划标签下的题目涵盖了各种难度级别和场景的问题。从简单的斐波那契数列、迷宫问题到可以用于实际应用的背包问题、最长公共子序列等,难度不断递进且话题丰富,有助于开发人员掌握动态规划的实际应用技能和抽象思维模式。 因此,深入LeetCode动态规划分类下的题目学习和练习,对于程序员的职业发展和技能提升有着重要的意义。 ### 回答3: 动态规划是一种常见的算法思想,它通过将问题拆分成子问题的方式进行求解。在LeetCode中,动态规划标签涵盖了众多经典和优美的算法问题,例如斐波那契数列、矩阵链乘法、背包问题等。 动态规划的核心思想是“记忆化搜索”,即将中间状态保存下来,避免重复计算。通常情况下,我们会使用一张二维表来记录状态转移过程中的中间值,例如动态规划求解斐波那契数列问题时,就可以定义一个二维数组f[i][j],代表第i项斐波那契数列中,第j个元素的值。 在LeetCode中,动态规划标签下有众多难度不同的问题。例如,经典的“爬楼梯”问题,要求我们计算到n级楼梯的方案数。这个问题的解法非常简单,只需要维护一个长度为n的数组,记录到达每一级楼梯的方案数即可。类似的问题还有“零钱兑换”、“乘积最大子数组”、“通配符匹配”等,它们都采用了类似的动态规划思想,通过拆分问题、保存中间状态来求解问题。 需要注意的是,动态规划算法并不是万能的,它虽然可以处理众多经典问题,但在某些场景下并不适用。例如,某些问题的状态转移过程比较复杂,或者状态转移方程中存在多个参数,这些情况下使用动态规划算法可能会变得比较麻烦。此外,动态规划算法也存在一些常见误区,例如错用贪心思想、未考虑边界情况等。 总之,掌握动态规划算法对于LeetCode的学习和解题都非常重要。除了刷题以外,我们还可以通过阅读经典的动态规划书籍,例如《算法竞赛进阶指南》、《算法与数据结构基础》等,来深入理解这种算法思想。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值