410. Split Array Largest Sum
Given an array which consists of non-negative integers and an integer m, you can split the array into m non-empty continuous subarrays. Write an algorithm to minimize the largest sum among these m subarrays.
Input:
nums = [7,2,5,10,8]
m = 2
Output:
18
Explanation:
There are four ways to split nums into two subarrays.
The best way is to split it into [7,2,5] and [10,8],
where the largest sum among the two subarrays is only 18.
1.动态规划
这道题优先会往动态规划去考虑,因为这里的递推关系很好寻找;
设d[i][k]是将数组[i,end)部分至多拆分成k个子数组的情况下,这些子数组中最大和的最小值;那么有:
就是说对于一个数组可以把它拆分成1 + (k-1)两大块,前面的部分的值就是数组和,后面的是子问题的解,那么我们只需要最外层关于k循环就可以;
class Solution {
public int splitArray(int[] a, int m) {
long[] sum = new long[a.length+1];
for(int i = 1;i<sum.length;i++) sum[i] = sum[i-1] + a[i-1];
long total = sum[a.length];
long[] dp = new long[a.length+1];
for(int i = 0;i<a.length;i++) dp[i] = total - sum[i];
for(int k = 2;k<=m;k++){
for(int i = 0;i<=a.length-k;i++){
for(int j = i;j<=a.length+1-k;j++){
dp[i] = Math.min(dp[i],Math.max(sum[j+1]-sum[i],dp[j+1]));
}
}
}
return (int)dp[0];
}
}
time: O(N2∗m) O ( N 2 ∗ m )
space: O(N) O ( N )
二分搜索
答案里给出了一个非常巧妙的二叉搜索的方法。
首先对于一个给定数组,我们肯定只能找到某一个确切的最优解 ans ,也就是说对于一个数组我们所能实现的最小值是 ans , 假设
f(x)
f
(
x
)
表示某个数组是否能实现x ,那么显然有:
基于这个条件,而我们很容易确定这个数组 ans 可能的范围,那么我们可以借助f(x)来在这个范围内二分搜索;
其实真正的难点是对于 f(x) f ( x ) 的编写,这里用到了贪心算法:
对于i,如果这个点之前的子数组的和加上a[i]没有超过限制,那么我们肯定应该把这个点放进这个字数组来最大的利用前面的空间,也就是说在不增加子数组的数量下更多的分配数组元素;反之如果不行我们只能开辟一个新的子数组来存储当前元素;
class Solution {
int[] a;
int m;
public int splitArray(int[] nums, int x) {
m = x;
a = nums;
long l = 0;
long r = 0;
for(int i:nums){
l = Math.max(i,l);
r+=i;
}
while(l<=r){
long m =(l+r)>>1;
if(f(m)) r = m-1;
else l = m+1;
}
return (int)l;
}
public boolean f(long sum){
long cur = 0;
long count = 1;
for(int i:a){
if(i>sum) return false;
if(cur+i>sum){
cur = i;
count++;
if(count>m) return false;
}
else cur+=i;
}
return true;
}
}
Error:
1.数组虽然都是int ,但是求和之后可能会大于int,所以要声明为long;
2.一开始在f(x)里增加了 a[i]>x 时候返回false 的语句,但是实际上x的范围是我们自己选的,不可能出现这种情况,所以不需要