LeetCode410. 分割数组的最大值

LeetCode410. 分割数组的最大值

给定一个非负整数数组和一个整数 m,你需要将这个数组分成 m 个非空的连续子数组。设计一个算法使得这 m 个子数组各自和的最大值最小。
注意: 数组长度 n 满足以下条件: 1 ≤ n ≤ 1000 1 ≤ m ≤ min(50, n)
示例:
输入: nums = [7,2,5,10,8] m = 2
输出: 18
解释: 一共有四种方法将nums分割为2个子数组。
其中最好的方式是将其分为[7,2,5] 和 [10,8], 因为此时这两个子数组各自的和的最大值为18,在所有情况中最小。

解析:
  • 解法一:动态规划

    动态规划的思路很简单,但算法复杂度较高

    当一个数组nums,已知其前j个数,分k-1组的子数组最大和为dp[j][k-1]

    前i个数分为k组的子数组最大和dp[i][k]=Math.max( d p [ j ] [ k 1 ] dp[j][k-1] , n = k 2 i 1 n u m s [ n ] \sum^{i-1}_{n=k-2}nums[n] ),

    具体代码如下:

class Solution {

    public int splitArray(int[] nums, int m) {
        if(m==0){
            return 0;
        }
        int[][] dp = new int[nums.length][nums.length];
        for(int i=0;i<nums.length;i++){
            dp[i][i]=nums[i];
            for(int j=i+1;j<nums.length;j++){
                dp[i][j]=dp[i][j-1]+nums[j];
            }            
        }  
        if(m==1){        
            return dp[0][nums.length-1];
        }
        int[][] dpM = new int[nums.length][m+1];
        for(int i=0;i<nums.length;i++){
            dpM[i][1]=dp[0][i];
        }
        for(int k=2;k<=m;k++){
            for(int i=k-1;i<nums.length;i++){
                int res = Integer.MAX_VALUE;
                for( int j=k-2;j<=i-1;j++){
                    res=Math.min(res,Math.max(dpM[j][k-1],dp[j+1][i]));
                }
                dpM[i][k]=res;                
            }
        }
        return dpM[nums.length-1][m];
    }
}

解法二: 二分法

分析:

  1. 首先,对于这个问题,数组nums如果按照一个数分一组,那么最大和即为max(nums),如果所有数在同一组,那么最大和为sum(nums),而这两个值也就是所有可能解的上下界;
  2. 接着,选取一种中间值,按照这个中间值分组,使得每组和小于mid,且放入的元素最多,记录总分组数;
  3. 最后如果分组数大于给定的分组数,那么说明每组装少了,需要增大下界;如果分组数小于等于给定的分组数,那么说明每组还能装更多的数,也就是减小上界。

具体逻辑见代码注释:

public int splitArray(int[] nums, int m) {
        //二分法是一种基于假设最大值并搜索的方法找到的
        //max和min是最后结果所有可能值的上下限,如果可以一个数一组,那么结果是min,
    	//如果所有数一组,结果为max
        long min=0,max=0;
        //初始化min,max
        for(int i:nums){
            max+=i;
            if(i>min){
                min=i;
            }
        }
        long res = max;
        //开始搜索
        while(min<max){
            //这一轮循环认为每一组最大和为(min+max)/2
            long mid = (min+max)>>1;
            //sum存储每一组的和
            long sum=0;
            int group=1;
            for(int i: nums){
                //分组和超过了mid,则重新分一组
                if(sum+i>mid){
                    sum=i;
                    group++;
                }else{
                    sum+=i;
                }
            }
            //如果分组多了,那说明我们每组和小了,提高我们分组和范围的下界,
            //则可以让更多的数进入同一组
            if(group>m){
                min=mid+1;
            }else{
                //如果分组少了,那说明我们每组和大了,降低我们分组和范围的上界,
                //则可以让更少的数进入同一组
                res=Math.min(res,mid);
                max=mid-1;
            }
        }
        return (int)res;
    }
发布了54 篇原创文章 · 获赞 11 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/dong_W_/article/details/104993022
今日推荐