题目描述
思路解析动画文字版
下面 15 步动画会按主解代码推进,而不是泛泛讲题型。
1. 状态定义:最多 k=2 笔交易。对每笔 t 维护两态:buy[t]=已买入(持股)时最大收益、sell[t]=已卖出(空仓)时最大收益。表的 6 列是 6 天价格,4 行是 buy1/sell1/buy2/sell2。
2. 初值:开局还没买任何股票:buy1=buy2=-∞(非法持仓),sell1=sell2=0(没交易就是 0 收益)。哨兵 sell[0]=0 表示“做第 1 笔买入前的本金”。
3. d0 价=3 · 第 1 笔买入:第 0 天价 3。若此刻做第 1 笔买入:手里现金从 sell0=0 减去 3,得 buy1=-3。卖出态没法更新(还没买出),sell1 仍为 0。
4. d0 价=3 · 第 2 笔买入:第 2 笔买入依赖 sell1=0(第 1 笔已卖完的现金)。buy2 = 0 - 3 = -3,sell2 仍是 0。第 0 天处理完。
5. d1 价=2 · 更便宜, 买入更划算:第 1 天价 2,比第 0 天便宜。改成在这天买入更省:buy1 由 -3 抬到 -2,buy2 同样抬到 -2。两个卖出态还没遇到更高价,sell1=sell2=0 不变。
6. d2 价=6 · 第 1 笔卖出获利:第 2 天价 6。第 1 笔在价 2 买入、价 6 卖出:sell1 = buy1+6 = -2+6 = 4,第一笔利润锁定 4。buy1 维持 -2(更便宜的买点已记下)。
7. d2 价=6 · 第 2 笔接力卖出:第 2 笔此刻也可卖:sell2 = buy2+6 = -2+6 = 4。当前两笔都只用上了第一段行情,sell2 暂时也是 4。第 2 天处理完。
8. d3 价=5 · 第 2 笔买点再压低:第 3 天价 5。第 2 笔可以拿第 1 笔利润 sell1=4 当本金再买:4-5=-1,比原来的 -2 更高,buy2 升到 -1。这一步把“先赚一笔再投入”记进了状态。buy1/sell1/sell2 此刻无更优,保持不变。
9. d4 价=0 · 极低价, 两笔买点都刷新:第 4 天价 0(白送)。第 1 笔在这买几乎不花钱:buy1=0。第 2 笔用 sell1=4 的利润 0 成本买入:buy2 = 4-0 = 4。两个卖出态没更高价可卖,sell1=sell2=4 不变。
10. d5 价=3 · 第 1 笔尝试卖出:第 5 天价 3。第 1 笔若在价 0 买、价 3 卖只赚 3,不如已有的 sell1=4(价2买价6卖)。max 取大,sell1 保持 4。buy1 也无更优,仍 0。
11. d5 价=3 · 第 2 笔卖出, 答案诞生:第 2 笔买在价 0(buy2=4 含了第一笔利润)、卖在价 3:sell2 = buy2+3 = 4+3 = 7,比原来的 4 高。这正是“价2买价6卖赚4 + 价0买价3卖赚3”两笔合计 7。
12. 读出答案:全部 6 天扫完。答案 = 最多 2 笔、最后空仓的最大收益 = sell2 = 7。注意取 sell[k] 而非 sell[k-1]:用满 2 笔不会比用 1 笔差(多一笔随时可选择不买)。
13. 为什么是 buy[t] 依赖 sell[t-1]:关键链路:buy2 用 sell1 当本金(蓝格 sell1=4 → 橙格 buy2 在 d3 升到 -1)。这条 sell[t-1]→buy[t] 的依赖天然保证“买下一笔前必须先卖掉上一笔”,不会同时持多仓。
14. 边界: k 很大时退化为无限交易:当 k 大到 2k 超过天数(k ≥ n/2),交易次数其实不再是限制,等价于“能买卖无限次”。这时直接累加所有相邻上涨差即可,省去开 2k 个状态,避免 k 过大时空间爆炸。
15. 复杂度与本质:外层 n 天、内层 k 笔,时间 O(n·k);buy/sell 各 k+1 长,空间 O(k)。本题是“限定交易次数”的状态机 DP 模板:每多一个约束就多开一维状态(这里是交易笔数 t)。最终答案 sell2 = 7。
记住这题的代码骨架:题意约束先落到状态变量,再用循环或递归维护它。
参考代码
class Solution: def maxProfit(self, k, prices): n = len(prices) if not prices or k == 0: return 0 if k >= n // 2: ans = 0 for i in range(1, n): if prices[i] > prices[i - 1]: ans += prices[i] - prices[i - 1] return ans buy = [-10**18] * (k + 1) sell = [0] * (k + 1) for price in prices: for t in range(1, k + 1): buy[t] = max(buy[t], sell[t - 1] - price) sell[t] = max(sell[t], buy[t] + price) return sell[k]复杂度
- 时间复杂度:O(kn),每个价格更新 k 个状态
- 空间复杂度:O(k),滚动状态
易错点
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。再去主线里挑下一道练手。
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题