解题思路:
写本题,源自看到的官方的一句描述 “「将数组分割为 m 段,求……」是动态规划题目常见的问法。”,让我想起了之前写的 1043. 分隔数组以得到最大和,早期写的题解,风格更像一种草稿,但还是有很多扣友鼓励似的阅读点赞,感谢感谢,笔芯~
{:width=“350px”}{:align=“left”}
定义状态
d p [ i ] dp[i] dp[i]:数组的前 i i i 个数即 n u m s [ 0 , 1... i − 1 ] nums[0,1...i-1] nums[0,1...i−1],被切了 Y − 1 Y-1 Y−1 刀,分割成 Y Y Y 个数组,满足每个数组的个数最大值不超过 K K K,每个数组的值变成最大值,分割后的最大和,如上图,当被分成 Y = 3 Y=3 Y=3 个部分时,第一部分的最大值为 15 15 15,第二部分为 9 9 9,第三部分 10 10 10,每一部分的每个值都上升为当前部分的局部 m a x max max,红色字体为新的值,累加后,求其最大值
转移方程
{:width=“450px”}{:align=“left”}
要想求 d p [ i ] dp[i] dp[i],这是数组的前 i i i 个数即 n u m s [ 0 , 1... i − 1 ] nums[0,1...i-1] nums[0,1...i−1],被切了 Y − 1 Y-1 Y−1 刀,分割成 Y Y Y 个数组,满足每个数组的个数最大值不超过 K K K,每个数组的值变成最大值,分割后的最大和
- 求 d p [ i − 1 ] dp[i-1] dp[i−1],表示数组的前 i i i 个数即 n u m s [ 0 , 1... i − 2 ] nums[0,1...i-2] nums[0,1...i−2],第二部分是 n u m s [ i − 1 ] nums[i-1] nums[i−1],也就是说 d p [ i − 1 ] dp[i-1] dp[i−1] + m a x ( n u m s [ i − 1 ] ) max(nums[i-1]) max(nums[i−1])* ( i − ( i − 1 ) ) (i-(i-1)) (i−(i−1))
- 求 d p [ i − 2 ] dp[i-2] dp[i−2],表示数组的前 i − 1 i-1 i−1 个数即 n u m s [ 0 , 1... i − 3 ] nums[0,1...i-3] nums[0,1...i−3],第二部分是 n u m s [ i − 2... i − 1 ] nums[i-2...i-1] nums[i−2...i−1],也就是说 d p [ i − 2 ] dp[i-2] dp[i−2] + m a x ( n u m s [ i − 2... i − 1 ] ) max(nums[i-2...i-1]) max(nums[i−2...i−1]) * ( i − ( i − 2 ) ) (i-(i-2)) (i−(i−2))
- 求 d p [ i − 3 ] dp[i-3] dp[i−3],表示数组的前 i − 2 i-2 i−2 个数即 n u m s [ 0 , 1... i − 4 ] nums[0,1...i-4] nums[0,1...i−4],第二部分是 n u m s [ i − 3... i − 1 ] nums[i-3...i-1] nums[i−3...i−1],也就是说 d p [ i − 3 ] dp[i-3] dp[i−3] + m a x ( n u m s [ i − 3... i − 1 ] ) max(nums[i-3...i-1]) max(nums[i−3...i−1]) * ( i − ( i − 3 ) ) (i-(i-3)) (i−(i−3))
- …
- 求 d p [ 0 ] dp[0] dp[0],表示数组的前 1 1 1 个数即 n u m s [ 0 , 0 ] nums[0,0] nums[0,0],第二部分是 n u m s [ 0... i − 1 ] nums[0...i-1] nums[0...i−1],也就是说 d p [ 0 ] dp[0] dp[0] + m a x ( n u m s [ 0... i − 1 ] ) max(nums[0...i-1]) max(nums[0...i−1]) * ( i − ( 0 ) ) (i-(0)) (i−(0))
求上面的的最大值
可以推导出 d p [ i ] dp[i] dp[i]= m a x max max( d p [ i ] dp[i] dp[i], d p [ j ] + ( i − j ) ∗ M A X dp[j]+(i-j)*MAX dp[j]+(i−j)∗MAX),其中 M A X MAX MAX 是 n u m s [ j . . . i − 1 ] nums[j...i-1] nums[j...i−1] 范围内的局部最大值,一旦找到最大值,该范围内的所有值都改成这个局部最大值 M A X MAX MAX,其中 0=< j j j< i i i
初始化边界
i − j i-j i−j 如果大于 K K K,后面 n u m s [ i . . . n ] nums[i...n] nums[i...n] 这部分将没有办法被涵盖进来,一个条件: i − j i-j i−j<= K K K
j j j>=0,这个没啥好说的
d p dp dp 初始化的时候容量为 n + 1 n+1 n+1,要求的 d p [ n ] dp[n] dp[n] 表示数组的前 n n n 个数即 n u m s [ 0 , 1... n ] nums[0,1...n] nums[0,1...n],被切了 Y − 1 Y-1 Y−1 刀,分割成 Y Y Y 个数组,满足每个数组的个数最大值不超过 K K K,每个数组的值变成最大值,分割后的最大和
编码技巧
- i i i 从左到右遍历, j j j 起始为 i − 1 i-1 i−1 从右往左遍历
- 注意记录局部的最大值 M A X MAX MAX
完整代码
public int maxSumAfterPartitioning(int[] A, int K) {
int n = A.length;
int[] dp = new int[n + 1];
for (int i = 0; i <= n; i++) {
int j = i - 1;
int max = dp[i];
while ((i - j) <= K && j >= 0) {
max = Math.max(max, A[j]);
dp[i] = Math.max(dp[i], dp[j] + (i - j) * max);
j--;
}
}
return dp[n];
}
class Solution {
public:
int maxSumAfterPartitioning(vector<int>& A, int K) {
// f[i] = max(f[j] + (i-j)*max(A[j..i]))
int n = A.size();
vector<int> f(n+1);
for (int i = 0; i <= n; i++) {
int curMax = 0;
// consider past integers [j...i]
for (int j = i-1; (i-j)<=K && j>=0; j--) {
curMax = max(curMax, A[j]);
f[i] = max(f[i], f[j] + (i-j)*curMax);
}
}
return f[n];
}
};
总结
- 动态规划的题目,需要想到一些基本的情况,可以像数学归纳法一样思考