问题描述
小R近期表现出色,公司决定以股票的形式给予奖励,并允许他在市场上进行交易以最大化收益。给定一个数组,数组中的第 i 个元素代表第 i 天的股票价格。小R需要设计一个算法来实现最大利润。
股票交易规则如下:
- 小R可以多次买卖股票,但在买入新的股票前必须卖出之前的股票。
- 每次卖出股票后存在一天的冷冻期,在冷冻期内小R不能购买股票。
你的任务是帮助小R计算出在遵守交易规则的情况下能够获得的最大利润。
stocks: 一个整数列表,表示连续几天内的股票价格。
测试样例
样例1:
输入:
stocks = [1, 2]
输出:1
样例2:
输入:
stocks = [2, 1]
输出:0
样例3:
输入:
stocks = [1, 2, 3, 0, 2]
输出:3
样例4:
输入:
stocks = [2, 3, 4, 5, 6, 7]
输出:5
样例5:
输入:
stocks = [1, 6, 2, 7, 13, 2, 8]
输出:12
解题思路
这是一个典型的动态规划问题。我们可以通过状态机的方法设计一个算法,用以跟踪每一天结束时可能存在的状态,并在这些状态下维护最大利润。
状态定义
假设共有三种状态:
- 持有状态(Hold):当天结束时持有一支股票。
- 冷冻期状态(Cooldown):当天结束时处于冷冻期,即当天卖出了一支股票。
- 非持有非冷冻期状态(Not Hold):当天结束时未持有股票,且当天不处于冷冻期。
状态变量:
hold[i]:第i天结束时,处于持有状态的最大利润。cooldown[i]:第i天结束时,处于冷冻期的最大利润。not_hold[i]:第i天结束时,处于非持有非冷冻期的最大利润。
状态转移方程
-
持有状态:
- 可以是从前一天的持有状态延续(没有买入或卖出)。
- 可以是当天买入股票(从非持有非冷冻期状态转移而来)。
hold[i]=max(hold[i−1],not_hold[i−1]−prices[i])hold[i] = \max(hold[i-1], not_hold[i-1] - prices[i])
-
冷冻期状态:
- 只能从前一天的持有状态卖出股票进入冷冻期。
cooldown[i]=hold[i−1]+prices[i]cooldown[i] = hold[i-1] + prices[i]
-
非持有非冷冻期状态:
- 可以是从前一天的非持有非冷冻期状态延续。
- 可以是从前一天的冷冻期状态转移(冷冻期结束)。
not_hold[i]=max(not_hold[i−1],cooldown[i−1])not_hold[i] = \max(not_hold[i-1], cooldown[i-1])
初始状态
- 第一天持有股票的利润为负,即
hold[0] = -prices[0]。 - 第一天不可能处于冷冻期状态,
cooldown[0] = 0。 - 第一天没有持有股票且非冷冻期状态的利润为零,
not_hold[0] = 0。
结果计算
最终答案为最后一天处于非持有状态的最大利润,即:
result=max(not_hold[n−1],cooldown[n−1])\text{result} = \max(not_hold[n-1], cooldown[n-1])
代码实现
以下是上述动态规划思路的Python实现:
def solution(prices):
if not prices:
return 0
n = len(prices)
# 初始化状态
hold = -prices[0] # 持有股票的最大利润
not_hold = 0 # 不持有且非冷冻期的最大利润
cooldown = 0 # 冷冻期的最大利润
for i in range(1, n):
prev_hold = hold
prev_not_hold = not_hold
prev_cooldown = cooldown
# 更新持有状态
hold = max(prev_hold, prev_not_hold - prices[i])
# 更新不持有状态
not_hold = max(prev_not_hold, prev_cooldown)
# 更新冷冻期状态
cooldown = prev_hold + prices[i]
# 最终的最大利润是在不持有状态和冷冻期状态中取最大值
return max(not_hold, cooldown)
if __name__ == "__main__":
# You can add more test cases here
print(solution([1, 2]) == 1 )
print(solution([2, 1]) == 0 )
print(solution([1, 2, 3, 0, 2]) == 3 )
print(solution([2, 3, 4, 5, 6, 7]) == 5 )
print(solution([1, 6, 2, 7, 13, 2, 8]) == 12 )
复杂度分析
- 时间复杂度:O(n),其中
n是股票价格数组的长度。只需遍历一次数组。 - 空间复杂度:O(1)。使用常数个变量记录状态。
总结与反思
-
算法优点:
- 动态规划设计清晰,将问题分解为多个可管理的子问题。
- 空间复杂度优化到
O(1),节省了内存使用。
-
算法扩展性:
- 该算法可扩展到其他类似问题,如加入交易次数限制或更复杂的冷冻期规则。
-
反思:
- 初始设计中曾遗漏冷冻期状态的更新逻辑,导致部分测试样例出错。通过检查状态转移方程并补全测试用例,最终解决问题。