问题描述
小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
问题分析
对于这个问题,我们可以拆解为每一天发生了什么,而这样思考显然没有出于整体的思想,所以在思考时应当不仅仅考虑当前情况,还要考虑前几天的情况,比如在考虑第五天的利润,需要考虑的是第0至4天的利润加第五天的利润。需要注意的是,这里的利润指的是:
1.假设当天未买未卖,则利润为0。
2.假设当天卖了,则利润为stocks[i]。
3.假设当天买了,则利润为-stocks[i]。
我们定义dfs(i,0)表示第i天结束时,未持有股票的最大利润。
dfs(i,1)表示第i天结束时,持有股票的最大利润。
图片来自b站:灵茶山艾府
递归边界
dfs(-1,0)=0,第0天开始时未持有股票,利润为0
dfs(-1,1)=-inf ,第0天开始时不可能持有股票,是一个不合法的情况
递归入口
dfs(i-1,0),因为最后一天结束时持有股票时不可能使利润最大化。
冻结情况
冻结,也就是买入时前一天不能卖出股票,那么本来 dfs(i,1) = dfs(i-1,0)-stocks[i]可以直接转移至dfs(i,1) = dfs(i-2,0)-stocks[i]
代码实现
- 时间复杂度:O(n)
- 空间复杂度:O(n)
本题总结
1. 动态规划:
- 使用动态规划来解决这个问题,定义一个递归函数 f(i, hold, cooldown),表示在第 i 天,是否持有股票 (hold) 以及是否处于冷冻期 (cooldown) 时的最大利润。
- i 表示当前天数,hold 表示是否持有股票,cooldown 表示是否处于冷冻期。
2. 状态转移:
- 如果 i < 0,表示天数无效,返回 0 或 -inf 取决于是否持有股票。
- 如果 cooldown 为 True,表示当前处于冷冻期,不能进行任何交易,直接返回 f(i-1, False, False)。
- 如果 hold 为 True,表示当前持有股票,可以选择继续持有或卖出(卖出后进入冷冻期)。
- 如果 hold 为 False,表示当前不持有股票,可以选择继续不持有或买入(买入后持有股票)。
3. 缓存优化:
- 使用 functools.lru_cache 来缓存递归调用的结果,避免重复计算,提高效率。