410. Split Array Largest Sum

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个子数组的情况下,这些子数组中最大和的最小值;那么有:

dp[i][k]=max(sum[i,j],dp[j+1][k1])(j>=i) d p [ i ] [ k ] = m a x ( s u m [ i , j ] , d p [ j + 1 ] [ k − 1 ] ) ( ∀ j >= i )

就是说对于一个数组可以把它拆分成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(N2m) O ( N 2 ∗ m )
space: O(N) O ( N )


二分搜索

答案里给出了一个非常巧妙的二叉搜索的方法。
首先对于一个给定数组,我们肯定只能找到某一个确切的最优解 ans ,也就是说对于一个数组我们所能实现的最小值是 ans , 假设 f(x) f ( x ) 表示某个数组是否能实现x ,那么显然有:

f(x)=true(x>=ans)f(x)=false(x<ans) f ( x ) = t r u e ( ∀ x >= a n s ) f ( x ) = f a l s e ( ∀ x < a n s )

基于这个条件,而我们很容易确定这个数组 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的范围是我们自己选的,不可能出现这种情况,所以不需要

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值