题目链接
https://leetcode-cn.com/problems/split-array-largest-sum/
题目描述
给定一个非负整数数组 nums
和一个整数 m
,你需要将这个数组分成 m
个非空的连续子数组。
设计一个算法使得这 m
个子数组各自和的最大值最小。
示例
输入:nums = [7,2,5,10,8], m = 2
输出:18
解释:
一共有四种方法将 nums 分割为 2 个子数组。
其中最好的方式是将其分为 [7,2,5] 和 [10,8] 。
因为此时这两个子数组各自的和的最大值为18,在所有情况中最小。
输入:nums = [1,2,3,4,5], m = 2
输出:9
输入:nums = [1,4,4], m = 3
输出:4
限制
1 <= nums.length <= 1000
0 <= nums[i] <= 10^6
1 <= m <= min(50, nums.length)
解题思路
对于数组的前 i i i个元素,将其划分为 j j j段的连续子数组,对于每个子数组求和,这些和的最大值表示为 m a x a max_a maxa。 a a a表示第 a a a种划分方法。 f [ i ] [ j ] f[i][j] f[i][j]是所有 m a x a max_a maxa中最小值。
因此可以得到以下状态转移方程:
f
[
i
]
[
j
]
=
m
i
n
k
=
0
i
−
1
{
m
a
x
(
f
[
k
]
[
j
−
1
]
,
s
u
m
{
k
+
1
,
i
}
)
}
f[i][j] =min_{k = 0}^{i-1}\{max( f[k][j - 1], sum\{k+1, i\} )\}
f[i][j]=mink=0i−1{max(f[k][j−1],sum{k+1,i})}
初始化:
f
[
0
]
[
0
]
=
0
f
[
i
]
[
j
]
=
∞
i
f
i
<
j
o
r
(
i
!
=
0
a
n
d
j
=
=
0
)
f[0][0] = 0 \\ f[i][j] = \infty \ if \ i < j \ or \ (i != 0 \ and \ j==0 )
f[0][0]=0f[i][j]=∞ if i<j or (i!=0 and j==0)
不太好讲为什么,可以试着写一下就知道了,假设
i
=
j
=
4
i = j = 4
i=j=4更新的时候一列一列从下往上更新
0 | ∞ \infty ∞ | ∞ \infty ∞ | ∞ \infty ∞ | ∞ \infty ∞ |
---|---|---|---|---|
∞ \infty ∞ | a [ 0 ] a[0] a[0] | ∞ \infty ∞ | ∞ \infty ∞ | ∞ \infty ∞ |
∞ \infty ∞ | a [ 0 ] + a [ 1 ] a[0] + a[1] a[0]+a[1] | m i n { ∞ , m a x ( f [ 1 ] [ 1 ] , a [ 1 ] ) } min\{\infty, max(f[1][1], a[1])\} min{∞,max(f[1][1],a[1])} | ∞ \infty ∞ | ∞ \infty ∞ |
∞ \infty ∞ | a [ 0 ] + a [ 1 ] + a [ 2 ] a[0] + a[1] + a[2] a[0]+a[1]+a[2] | m i n { ∞ , m a x ( f [ 1 ] [ 1 ] , a [ 1 ] + a [ 2 ] ) , m a x ( f [ 2 ] [ 1 ] , a [ 2 ] ) } min\{\infty, max(f[1][1], a[1] + a[2]), \\ max(f[2][1], a[2])\} min{∞,max(f[1][1],a[1]+a[2]),max(f[2][1],a[2])} | m i n { ∞ , ∞ , m a x ( f [ 2 ] [ 2 ] , a [ 2 ] ) } min\{\infty,\infty,\\max(f[2][2],a[2]) \} min{∞,∞,max(f[2][2],a[2])} | ∞ \infty ∞ |
∞ \infty ∞ | a [ 0 ] + a [ 1 ] + a [ 2 ] + a [ 3 ] a[0] + a[1] + \\ a[2] + a[3] a[0]+a[1]+a[2]+a[3] | m i n { ∞ , m a x ( f [ 1 ] [ 1 ] , a [ 1 ] + a [ 2 ] + a [ 3 ] ) , m a x ( f [ 2 ] [ 1 ] , a [ 2 ] + a [ 3 ] ) } , m a x ( f [ 3 ] [ 1 ] , a [ 3 ] ) } min\{\infty,max(f[1][1], a[1] + a[2] + a[3]), \\ max(f[2][1], a[2] + a[3])\}, max(f[3][1], a[3])\} min{∞,max(f[1][1],a[1]+a[2]+a[3]),max(f[2][1],a[2]+a[3])},max(f[3][1],a[3])} | m i n { ∞ , ∞ , m a x ( f [ 2 ] [ 2 ] , a [ 2 ] + a [ 3 ] ) , m a x ( f [ 3 ] [ 2 ] , a [ 3 ] ) } min\{\infty, \infty, \\ max(f[2][2],a[2]+a[3]) \\,max(f[3][2],a[3]) \} min{∞,∞,max(f[2][2],a[2]+a[3]),max(f[3][2],a[3])} | m i n { ∞ , ∞ , ∞ , , m a x ( f [ 3 ] [ 3 ] , a [ 3 ] ) } min\{\infty, \infty, \infty, \\, max(f[3][3], a[3]) \} min{∞,∞,∞,,max(f[3][3],a[3])} |
Code
class Solution {
public static int MaxValue = 1000_000_001;
public int splitArray(int[] nums, int m) {
int[][] dp = new int[nums.length + 1][m + 1];
int[] sub = new int[nums.length + 1];
for(int i = 0; i < nums.length + 1; i++){
for(int j = 0; j < m + 1; j++){
if(i < j || (i != 0 && j == 0)){
dp[i][j] = MaxValue;
continue;
}
dp[i][j] = 0;
}
}
int sum = 0;
sub[0] = 0;
for(int i = 0; i < nums.length; i++){
sum += nums[i];
sub[i + 1] = sum;
}
for(int j = 1; j < m + 1; j++){ // 第j列
for(int i = j; i < nums.length + 1; i++){ //第i行
int max_value = MaxValue;
for(int k = 0; k <= i - 1; k++){
int cur_sum = sub[i] - sub[k];
int cur_max = Math.max(dp[k][j - 1], cur_sum);
max_value = Math.min(cur_max, max_value);
}
dp[i][j] = max_value;
}
}
return dp[nums.length][m];
}
}