Maximum Subarray III
Maximum Subarray III (leetcodelintcode)
Description
Given an array of integers and a number k,
find k non-overlapping subarrays which have the largest sum.
The number in each subarray should be contiguous.
Return the largest sum.
Notice
The subarray should contain at least one number
Example
Given [-1,4,-2,3,-2,3], k=2, return 8
解题思路
本题目是典型的划分型动态规划。
一、常规解法
- 定义状态:定义二维状态变量
f[i][j],表示前i个数字,取j个子数组时,得到的最大和。 - 定义状态转移函数:对于当前状态
f[i][j],由于划分是任意的,所以不止和当前的元素相关。其上一个状态可能为f[m][j - 1],考虑到i>= j,m的取值范围是j - 1<= m<i,则有f[i][j] = max{f[m][j - 1] + maxSubArray(m + 1, i)}。 - 定义起点:
f[i][0]从前i个数字,0个子数组,得到的最大和为0。f[0][i]从前0个数字,i个子数组,得到的最大和为0。
- 定义终点:最终结果即为
f[n][k]。
算法复杂度
- 时间复杂度:
O(k*n^3)。 - 空间复杂度:
O(kn)。
易错点
- 涉及划分时,需要注意几个自变量
i, j, m之间的大小关系。
Java 实现
public
class
Solution
{
/**
*
@param
nums: A list of integers
*
@param
k: An integer denote to find k non-overlapping subarrays
*
@return
: An integer denote the sum of max k non-overlapping subarrays
*/
public
int
maxSubArray
(
int
[] nums,
int
k)
{
if
(nums ==
null
|| nums.length ==
0
|| k
<
=
0
|| k
>
nums.length) {
return
-
1
;
}
int
n = nums.length;
// status
int
[][] f =
new
int
[n +
1
][k +
1
];
// initialize
for
(
int
j =
1
; j
<
= k; j++) {
for
(
int
i = j; i
<
= n; i++) {
f[i][j] = Integer.MIN_VALUE;
for
(
int
m = j-
1
; m
<
i; m++) {
f[i][j] = Math.max(f[i][j], f[m][j-
1
] + maxSubArray(nums, m, i-
1
));
}
}
}
return
f[n][k];
}
private
int
maxSubArray
(
int
[] nums,
int
start,
int
end)
{
int
curSum =
0
;
int
maxSum = Integer.MIN_VALUE;
for
(
int
i = start; i
<
= end; i++) {
curSum = Math.max(curSum + nums[i], nums[i]);
maxSum = Math.max(maxSum, curSum);
}
return
maxSum;
}
}
时间复杂度太高,进一步思考,是否可以简化求数组最大和的操作,考虑到一个数组从两个方向求最大和子数组的结果是一样的,所以把maxSubArray函数和自变量m循环结合在一起,将时间复杂度将至O(k*n^2)。
Java 实现
public
class
Solution
{
/**
*
@param
nums: A list of integers
*
@param
k: An integer denote to find k non-overlapping subarrays
*
@return
: An integer denote the sum of max k non-overlapping subarrays
*/
public
int
maxSubArray
(
int
[] nums,
int
k)
{
if
(nums ==
null
|| nums.length ==
0
|| k
<
=
0
|| k
>
nums.length) {
return
-
1
;
}
int
n = nums.length;
// status
int
[][] f =
new
int
[n +
1
][k +
1
];
// initialize
for
(
int
j =
1
; j
<
= k; j++) {
for
(
int
i = j; i
<
= n; i++) {
f[i][j] = Integer.MIN_VALUE;
int
curSum =
0
;
int
maxSum = Integer.MIN_VALUE;
for
(
int
m = i-
1
; m
>
= j-
1
; m--) {
curSum = Math.max(curSum + nums[m], nums[m]);
maxSum = Math.max(maxSum, curSum);
f[i][j] = Math.max(f[i][j], f[m][j-
1
] + maxSum);
}
}
}
return
f[n][k];
}
}
二、划分型动规解法
对方法一进行思考,其实仍然可以进行优化。对于f[i][j] = max{f[m][j - 1] + maxSubArray(m + 1, i)}, j - 1 <= m < i,每当新增一个元素的时候,之前的最大值仍是最大值,但是需要和新加进来的元素进行对比,所以只需要把之前遍历得到的结果用一个变量存储起来,就无需反复遍历了。
- 定义状态:
- 定义
localMax[i][j],表示前i个数字,取j个划分时,包含第i个数字的最大和。 - 定义
globalMax[i][j],表示前i个数字,取j个划分时,可以不包含第i个数字的最大和。
- 定义
- 定义状态转移函数:
- 对
localMax[i][j],由于一定包含第i个数字,那么有两种情况,- 第
i个数字属于第j个子数组,对应localMax[i - 1][j] + nums[i - 1]; - 第
i个数字不属于第j个子数组,对应localMax[i - 1][j - 1] + nums[i - 1]; - 比较两者取最大值皆可。
- 第
- 对
globalMax[i][j],比较localMax[i][j]和globalMax[i - 1][j]中的最大值即可。
- 对
- 定义起点:
- 对
localMax[i][j],在处理时会出现localMax[i - 1][i],根据逻辑此种情况不存在,由于涉及取最大值操作,所以初始化为Integer.MIN_VALUE。
- 对
- 定义终点:最终结果即为
globalMax[n][k]。
算法复杂度
- 时间复杂度:
O(nk)。 - 空间复杂度:
O(nk)。
注:本题目可以对localMax[i][j]进行滚动数组优化。
Java 实现
public
class
Solution
{
/**
*
@param
nums: A list of integers
*
@param
k: An integer denote to find k non-overlapping subarrays
*
@return
: An integer denote the sum of max k non-overlapping subarrays
*/
public
int
maxSubArray
(
int
[] nums,
int
k)
{
if
(nums ==
null
|| nums.length ==
0
|| k
<
=
0
|| k
>
nums.length) {
return
-
1
;
}
int
n = nums.length;
// status
int
[][] globalMax =
new
int
[n +
1
][k +
1
];
int
[][] localMax =
new
int
[n +
1
][k +
1
];
// initialize
for
(
int
j =
1
; j
<
= k; j++) {
localMax[j -
1
][j] = Integer.MIN_VALUE;
for
(
int
i = j; i
<
= n; i++) {
localMax[i][j] = Math.max(localMax[i -
1
][j], globalMax[i -
1
][j -
1
]) + nums[i -
1
];
if
(i == j) {
globalMax[i][j] = localMax[i][j];
}
else
{
globalMax[i][j] = Math.max(globalMax[i -
1
][j], localMax[i][j]);
}
}
}
return
globalMax[n][k];
}
}