2026 编程趋势 配套资源清单(免费为主,直接取用)
2026/5/31 12:04:05
给你一个整数数组prices,其中prices[i]是第 i 天股票的价格,以及一个整数k。
你最多可以进行k 笔交易,每笔交易可以是以下任一类型:
prices[j] - prices[i]prices[i] - prices[j]限制条件:
返回可以获得的最大总利润。
示例 1:
输入: prices = [1,7,9,8,2], k = 2 输出: 14 解释: - 普通交易:第 0 天买入(1),第 2 天卖出(9),利润 = 8 - 做空交易:第 3 天卖出(8),第 4 天买回(2),利润 = 6 - 总利润 = 8 + 6 = 14示例 2:
输入: prices = [12,16,19,19,8,1,19,13,9], k = 3 输出: 36 解释: - 普通交易:买入(12),卖出(19),利润 = 7 - 做空交易:卖出(19),买回(8),利润 = 11 - 普通交易:买入(1),卖出(19),利润 = 18 - 总利润 = 7 + 11 + 18 = 36这道题的关键点在于:
使用三维 DP 数组:dp[i][j][s]
i:当前是第 i 天(0 ≤ i < n)j:已完成的交易数(0 ≤ j ≤ k)s:当前状态s = 0:空仓(不持有任何头寸)s = 1:做多持有(已买入,等待卖出)s = 2:做空持有(已卖出,等待买回)dp[i][j][s]表示:第 i 天、完成 j 笔交易、处于状态 s 时的最大收益。
dp[i][j][0]可以从以下状态转移而来:
dp[i-1][j][0]dp[i-1][j][1] + prices[i](完成第 j 笔交易)dp[i-1][j][2] - prices[i](完成第 j 笔交易)dp[i][j][0] = max(dp[i-1][j][0], dp[i-1][j][1] + prices[i], dp[i-1][j][2] - prices[i])注意:平仓操作会完成一笔交易,所以从j状态平仓。
dp[i][j][1]可以从以下状态转移而来:
dp[i-1][j][1]dp[i-1][j-1][0] - prices[i](开始新交易)dp[i][j][1] = max(dp[i-1][j][1], dp[i-1][j-1][0] - prices[i])注意:开始新交易时,从j-1的空仓状态转移(因为这笔交易还未完成)。
dp[i][j][2]可以从以下状态转移而来:
dp[i-1][j][2]dp[i-1][j-1][0] + prices[i](开始新交易)dp[i][j][2] = max(dp[i-1][j][2], dp[i-1][j-1][0] + prices[i])第 0 天的状态:
dp[0][0][0] = 0:初始空仓,收益为 0dp[0][j][1] = -prices[0]:第 0 天买入,成本为负dp[0][j][2] = prices[0]:第 0 天卖空,收益为正对于所有j ∈ [1, k],都初始化持有状态,这样可以简化边界处理。
dp[n-1][k][0]:最后一天、完成 k 笔交易、空仓状态的最大收益。
funcmaximumProfit(prices[]int,kint)int64{n:=len(prices)dp:=make([][][]int64,n)fori:=rangedp{dp[i]=make([][]int64,k+1)forj:=rangedp[i]{dp[i][j]=make([]int64,3)}}// 初始化第 0 天的状态forj:=1;j<=k;j++{dp[0][j][1]=int64(-prices[0])dp[0][j][2]=int64(prices[0])}fori:=1;i<n;i++{forj:=1;j<=k;j++{dp[i][j][0]=max(dp[i-1][j][0],max(dp[i-1][j][1]+int64(prices[i]),dp[i-1][j][2]-int64(prices[i])))dp[i][j][1]=max(dp[i-1][j][1],dp[i-1][j-1][0]-int64(prices[i]))dp[i][j][2]=max(dp[i-1][j][2],dp[i-1][j-1][0]+int64(prices[i]))}}returndp[n-1][k][0]}funcmax(a,bint64)int64{ifa>b{returna}returnb}时间复杂度:O(n × k)
空间复杂度:O(n × k)
可以使用滚动数组优化,只保留前一天的状态,将空间复杂度降到 O(k):
funcmaximumProfit(prices[]int,kint)int64{n:=len(prices)prev:=make([][]int64,k+1)curr:=make([][]int64,k+1)forj:=0;j<=k;j++{prev[j]=make([]int64,3)curr[j]=make([]int64,3)}forj:=1;j<=k;j++{prev[j][1]=int64(-prices[0])prev[j][2]=int64(prices[0])}fori:=1;i<n;i++{forj:=1;j<=k;j++{curr[j][0]=max(prev[j][0],max(prev[j][1]+int64(prices[i]),prev[j][2]-int64(prices[i])))curr[j][1]=max(prev[j][1],prev[j-1][0]-int64(prices[i]))curr[j][2]=max(prev[j][2],prev[j-1][0]+int64(prices[i]))}prev,curr=curr,prev}returnprev[k][0]}区分做多和做空:必须用两个不同的状态来表示,因为它们的收益计算方式不同
交易完成时机:从持有状态转到空仓状态时,才算完成一笔交易,交易数 +1
初始化技巧:对所有 j 都初始化持有状态,避免复杂的边界判断
状态转移简洁:用嵌套max函数,一行代码表达所有可能的转移
本题是最通用的版本,同时支持做多和做空,难度更高。