代码 1 动态规划:
动态转移方程
f[j][j]表示前 i 个数分割为 j 段所能得到的最大连续子数组和的最小值sub[i]表示前 i 个数的和,以此来得到一段字段的和
func splitArray(nums []int, m int) int {
n := len(nums)
f := make([][]int, n + 1)
sub := make([]int, n + 1)
for i := 0; i < len(f); i++ {
f[i] = make([]int, m + 1)
for j := 0; j < len(f[i]); j++ {
f[i][j] = math.MaxInt32
}
}
for i := 0; i < n; i++ {
sub[i + 1] = sub[i] + nums[i]
}
f[0][0] = 0
for i := 1; i <= n; i++ {
for j := 1; j <= min(i, m); j++ {
for k := j-1; k < i; k++ {
f[i][j] = min(f[i][j], max(f[k][j - 1], sub[i] - sub[k]))
}
}
}
return f[n][m]
}
func min(x, y int) int {
if x < y {
return x
}
return y
}
func max(x, y int) int {
if x > y {
return x
}
return y
}
代码 2 二分 + 贪心:
- 当最大和 不大于 x 时,可以分割几段,这便是 check 函数的作用
- 我们可以一个一个试,在满足题意的前提下找到最小的 x 值,然后输出答案
- 而我们试的方式便是二分
func splitArray(nums []int, m int) int {
left, right := 0, 0
for i := 0; i < len(nums); i++ {
right += nums[i]
if left < nums[i] {
left = nums[i]
}
}
for left < right {
mid := (right - left) / 2 + left
if check(nums, mid, m) {
right = mid
} else {
left = mid + 1
}
}
return left
}
func check(nums []int, x, m int) bool {
sum, cnt := 0, 1
for i := 0; i < len(nums); i++ {
if sum + nums[i] > x {
cnt++
sum = nums[i]
} else {
sum += nums[i]
}
}
return cnt <= m
}