题目解析
在股票交易问题中,我们的目标是设计一个算法,帮助小R在遵守交易规则的情况下,获得最大利润。规则如下:
- 小R可以多次买卖股票,但在买入新的股票前,必须卖出之前的股票。
- 每次卖出股票后,存在一个冷冻期,意味着在冷冻期内不能买入新的股票。
我们需要处理的核心是如何在多次买卖中避免冷冻期的影响,并同时计算出在这些约束条件下,如何最大化收益。
思路分析
为了最大化利润,我们可以采用动态规划的思想来求解这个问题。具体来说,我们可以用两个状态数组 hold 和 cash 来分别记录在某一天结束时的状态:
hold[i]:表示第i天结束时,持有股票的最大利润。cash[i]:表示第i天结束时,不持有股票的最大利润。
对于 hold[i],有两种可能的状态:
- 前一天已经持有股票:那么今天继续持有,利润保持不变。
- 前天没有持有股票,并且在前前一天卖掉股票后今天重新购买股票,那么今天的利润会减去今天的股票价格。
对于 cash[i],也有两种可能的状态:
- 前一天已经不持有股票,今天继续不持有,利润保持不变。
- 前一天持有股票,在今天卖出股票,利润会增加今天的股票价格。
动态规划状态转移方程
hold[i] = max(hold[i - 1], cash[i - 2] - stocks[i]):表示第i天持有股票的最大利润。可以从前一天持有股票继续持有,也可以从两天前没有股票的状态中,今天购买股票。cash[i] = max(cash[i - 1], hold[i - 1] + stocks[i]):表示第i天不持有股票的最大利润。可以从前一天不持有股票继续不持有,或者从前一天持有股票并在今天卖出。
初始状态:
- 第一天持有股票的利润为
-stocks[0],即买入股票花费了价格。 - 第一天不持有股票的利润为
0。
代码详解
def solution(stocks):
n = len(stocks)
# 初始化两个状态数组
hold = [0] * n # hold[i] 表示第 i 天结束时持有股票的最大利润
cash = [0] * n # cash[i] 表示第 i 天结束时不持有股票的最大利润
# 第一天的初始化
hold[0] = -stocks[0] # 第一天买入股票,花费股票价格
cash[0] = 0 # 第一天不买股票,利润为 0
for i in range(1, n):
if i >= 2:
# 当前持有股票的最大利润:保持前一天的状态,或者在两天前不持有股票并在今天买入
hold[i] = max(hold[i - 1], cash[i - 2] - stocks[i])
else:
# 第二天持有股票的最大利润:保持前一天的状态,或者第二天买入股票
hold[i] = max(hold[i - 1], -stocks[i])
# 当前不持有股票的最大利润:保持前一天的状态,或者卖掉股票
cash[i] = max(cash[i - 1], hold[i - 1] + stocks[i])
return cash[-1] # 返回最后一天不持有股票时的最大利润
if __name__ == "__main__":
print(solution([1, 2]) == 1 ) # 输出 1
print(solution([2, 1]) == 0 ) # 输出 0
print(solution([1, 2, 3, 0, 2]) == 3 ) # 输出 3
print(solution([2, 3, 4, 5, 6, 7]) == 5 ) # 输出 5
print(solution([1, 6, 2, 7, 13, 2, 8]) == 12 ) # 输出 12
代码分析
-
初始化:
hold[0] = -stocks[0],表示第一天买入股票,获得的利润是负的,因为购买股票需要支付资金。cash[0] = 0,表示第一天没有交易,利润为0。
-
动态规划递推:
hold[i] = max(hold[i - 1], cash[i - 2] - stocks[i]):这里通过对比是否选择在i天买入股票(cash[i-2] - stocks[i])或者保持前一天持有的状态来决定。cash[i] = max(cash[i - 1], hold[i - 1] + stocks[i]):通过对比是否选择在i天卖出股票(hold[i-1] + stocks[i])来计算不持有股票时的最大利润。
-
返回结果:
- 最终结果为
cash[-1],即最后一天没有持有股票时的最大利润。
- 最终结果为
知识总结
在刷题的过程中,我学到了如何通过动态规划来解决类似于股票交易类的问题。动态规划通过引入状态数组来记录某一时刻的最佳选择,避免了重复计算,并有效地降低了时间复杂度。此类问题通常会涉及到以下几个方面:
- 状态定义:我们需要明确状态表示什么,通常是关于时间和行为的组合。例如,
hold[i]表示第i天持有股票的最大利润,cash[i]表示不持有股票的最大利润。 - 状态转移方程:如何通过当前的状态推导出下一个状态,这涉及到多个选择的对比。
- 边界条件:对于初始状态的处理,确保递推过程不出错。
对于其他入门同学的建议是:
- 在处理动态规划问题时,首先需要明确状态的定义,并尽量将状态转移方程写清楚。
- 不要忽视边界条件,特别是在数组和索引涉及到时,初学者容易在这些细节上出错。
- 多做题,积累经验,理解每道题背后的思路和规律。
总结
本题是一个典型的动态规划问题,利用 hold 和 cash 两个状态数组,模拟股票买卖过程中的最大利润。通过明确的状态转移方程和合理的初始状态设置,最终得到了正确的结果。对于类似的股票交易问题,可以通过动态规划来高效求解,并通过分析状态的转换关系来设计合适的算法。