限次交易最优收益的 2D 动态规划
Bounded K Transactions: 2D DP for Maximum Profit
开始编码某主观交易组手上有对一只股票未来 N 个交易日收盘价的完美预测序列 prices(按时间升序)。风控规定一条硬约束:在整个窗口内最多完成 K 次完整往返交易(一次「买入 → 卖出」算一次),多出的指令会被预交易拒掉。请算出在该交易额度下的最大整数收益——即给定上帝视角的价格曲线,挑哪几段做、放弃哪几段,能榨出多少钱。
请实现 solution(prices: list[int], k: int) -> int。规则:(a)任何时刻最多持仓一股,必须先全部卖掉才能再买;(b)允许做 0 次到 K 次任意整数次交易(不强制吃满),允许整段不交易;(c)期末必须空仓(不允许带仓离场);(d)若 len(prices) < 2、k == 0、或所有可行方案都不赚钱,应返回 0。
示例 1——k=0 禁止交易:solution([1, 5, 2, 8, 3], 0) 返回 0。即便桌面上摆着可赚的波段,预算为零,最优只能是「不交易」。
示例 2——k=1 退化为 LeetCode-121 单笔最优:solution([7, 1, 5, 3, 6, 4], 1) 返回 5。在谷底之后的全局最低价(第 1 天,价格 1)买入,在波峰(第 4 天,价格 6)卖出。
示例 3——k=2 是「最多两次交易」的标准题:solution([3, 2, 6, 5, 0, 3], 2) 返回 7。最优执行:第 1 天买入 (-2)、第 2 天卖出 (+6)、第 4 天买入 (-0)、第 5 天卖出 (+3),合计 (6-2)+(3-0)=7。注意只取单笔最优(第 4 → 第 5,+3)或(第 1 → 第 2,+4)都比不上用足两笔额度——这正是二维 DP 必须同时跟踪「已用次数」的原因。
朴素地枚举「在哪几天买、在哪几天卖」是组合爆炸。关键观察是每天结束时的状态由两个维度刻画:「现在是否持仓」(2 种取值)与「已平多少次交易」(K+1 种取值),全局只有 2(K+1) 个状态,DP 表是 O(N·K) 时间、O(K) 滚动空间。这是带硬交易上限的组合优化的标准刻画:交易额度迫使你放弃小波段、保留额度去吃更大的趋势——和真实交易台在「机会成本」与「合规/运营硬上限」之间的取舍如出一辙。
约束条件
- 0 ≤ len(prices) ≤ 10000
- 0 ≤ k ≤ 100(k=0 时不允许任何交易)
- 0 ≤ prices[i] ≤ 100000(整数报价,单位:元/股)
- 同一时刻最多持仓一股;必须先卖才能再买
- 期末必须空仓(不允许带着仓位走出窗口)
- 若所有可行计划都亏损,返回 `0`(即「不交易」)
- 返回值为精确整数(无浮点容差)
样例
Case 1 · k=2 worked example
输入: [[3,2,6,5,0,3],2]
期望: 7
K=2。最优执行:第 1 天买 (-2)、第 2 天卖 (+6)、第 4 天买 (-0)、第 5 天卖 (+3),累计 (6-2)+(3-0)=7。这是「最多两次交易」的标准案例。
Case 2 · k=1 collapses to best single trade
输入: [[7,1,5,3,6,4],1]
期望: 5
K=1 时退化为「最低买入、最高卖出」:第 1 天买入 (1)、第 4 天卖出 (6),单笔净赚 5。
最近提交
还没有提交记录。
编码区
实现 solution(...)。本地运行当前支持 Python 可见样例;服务端提交会运行可见样例和隐藏测试。
默认展示公开样例。点击「运行样例」后会在这里显示实际输出;点击「提交评测」会进入隐藏测试。
Case 1 · k=2 worked example
输入: [[3,2,6,5,0,3],2]
期望: 7
K=2。最优执行:第 1 天买 (-2)、第 2 天卖 (+6)、第 4 天买 (-0)、第 5 天卖 (+3),累计 (6-2)+(3-0)=7。这是「最多两次交易」的标准案例。
Case 2 · k=1 collapses to best single trade
输入: [[7,1,5,3,6,4],1]
期望: 5
K=1 时退化为「最低买入、最高卖出」:第 1 天买入 (1)、第 4 天卖出 (6),单笔净赚 5。