动态规划 + 贪心
https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv/
- 最多只能做k次交易
- 不是说正好k次,而是k次以内的交易都行, 哪个最优
- 有个大过滤,如果K > N/2, 就等于无限次交易
为啥?
- 无限次交易的实质就是把每个上坡都求出来

- 整个数组最多上坡数也不会超过N/2, 看最频繁的
- 那么如果K > N/2了,也就相当于做无限次交易求最优了嘛,也就是122题买卖股票最佳时机Ⅱ
- 问题Ⅱ是把一个上坡分解成了多次算,我们也可以不分解呀
- 一个上坡上的多次交易是可以合成一笔交易的
- 问题Ⅱ是把一个上坡分解成了多次算,我们也可以不分解呀
- 所以我们接下来就讨论 K < N/2的情况
dp[ i ][ j ]含义: arr[0~i] 交易次数不超过j 次的最优
dp[ i ][ j ]
1) [ i ]不参加任何交易
dp[i - 1][ j ]
2) [ i ]参加交易
这里有一个贪心,就是i位置除了最后一次交易卖出时机之外其他都不考虑,
比如此时j = 3, i是第二次交易,那么第3次交易就废了呀,你第三次只能在i位置买入再卖出,第三次收益0
也就是i参加了前面的交易,那么后面的交易其实就废了。
所以这里的参加交易指的是参加最后一次交易的卖出时机,那么就可以细分了
- ① 最后一次交易的买入时机在[ i ]
0~i 上做 k- 1次交易 + [i] - [i]
- ② 最后一次交易的买入时机在[ i - 1]
0~i-1 上做 k-1次交易 + [ i ] - [i - 1]
- ③ 最后一次交易的买入时机在[ i - 2]
0~i-2 上做 k-1 次交易 + [ i ] - [i - 2]
- ④ 最后一次交易的买入时机在[ i - 3]
……..
枚举所有情况
综上
有枚举行为,可以斜率优化
- 在求dp[5][2]的时候, ②到⑥可能性其实可以看作方框内的[5]先不加, 它们6个人pk出一个max之后,这个max再去+[5]
然后我们再看dp[6][2]的时候,把[6]抠了,可以发现之前这些指标已经枚举过一次了

- 也就是 dp [ i ] [ j ] 的求解过程能加速 dp [ i + 1 ] [ j ] 的求解过程
写得清晰一点,这样看
未优化版本
public int maxProfit1(int k, int[] prices) {if (prices == null || prices.length == 0) {return 0;}int N = prices.length;if (k >= N / 2) {int ans = 0;for (int i = 1; i < prices.length; i++) {ans += Math.max(prices[i] - prices[i - 1], 0);}return ans;}int[][] dp = new int[N][k + 1];for (int i = 1; i < N; i++) {for (int j = 1; j <= k; j++) {dp[i][j] = dp[i - 1][j];for (int p = 0; p <= i; p++) {dp[i][j] = Math.max(dp[i][j], dp[p][j - 1] + prices[i] - prices[p]);}}}return dp[N - 1][k];}
优化版本
public int maxProfit(int k, int[] prices) {if (prices == null || prices.length == 0) {return 0;}int N = prices.length;int ans = 0;if (k >= N / 2) {for (int i = 1; i < prices.length; i++) {ans += Math.max(prices[i] - prices[i - 1], 0);}return ans;}int[][] dp = new int[N][k + 1];for (int j = 1; j <= k; j++) {int best = dp[0][j - 1] - prices[0];for (int i = 1; i < N; i++) {dp[i][j] = Math.max(dp[i - 1][j], prices[i] + best);best = Math.max(best, dp[i][j - 1] - prices[i]);}}return dp[N - 1][k];}
