前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >410. 分割数组的最大值 Krains 2020-08-29 20:21:39 动态规划二分查找

410. 分割数组的最大值 Krains 2020-08-29 20:21:39 动态规划二分查找

作者头像
Krains
发布2020-09-10 18:38:21
4060
发布2020-09-10 18:38:21
举报
文章被收录于专栏:Krains

题目链接

二分查找

对答案进行二分,得到mid,如果mid可以将数组切割成m组,并且每组之和小于mid,由于我们要找的是满足要求的最小值,所以可以排除区间(mid, right],去[left, mid]找,如果不满足,那么区间[left, mid]中任一数必然也不满足条件,应到[mid+1, right]找。

切割时贪心地去尽可能将让一组的和尽可能大,只有这一组放不下的时候才新开一组。

代码语言:javascript
复制
class Solution {
    public int splitArray(int[] nums, int m) {
        int right = Integer.MAX_VALUE;
        int left = 0;
        while(left < right){
            int mid = left + (right - left) / 2;
            if(canSplit(nums, m, mid)){
                right = mid;
            }else{
                left = mid + 1;
            }
        }

        return left;
    }

    public boolean canSplit(int[] nums, int m, int sum){
        int s = 0;
        int count = 1;
        for(int num : nums){
            // 如果数组中某个数大于sum,无法完成分组
            if(num > sum)
                return false;
            // 如果num能够加入分组,则加入,否者新开一个分组
            if(s + num <= sum){
                s += num;
            }else{
                s = num;
                count++;
            }
        }

        return count <= m;
    }
}

复杂度分析

  • 时间复杂度:O(Nlog2I)O(Nlog_2I)O(Nlog2​I),I其实等于sum(nums)-min(nums),代码没有体现这一点,主要是由于lc没有给出nums元素的范围。
  • 空间复杂度:O(1)O(1)O(1)

动态规划

状态表示

  • 集合:使用(i,j)(i, j)(i,j)表示前j个数字分i组的所有分组的集合。
  • 属性:一个集合就是一个分组,取各个小组之和有一个最大值,每个集合都有这么一个最大值,f(i,j)f(i,j)f(i,j)表示的就是这些集合中最大值的最小值。

状态计算/集合划分

  • 将元素[0, j)划分为第i组,f(i,j)=max(f(i−1,0),sum(nums[0,j)))f(i, j) = max(f(i-1, 0), sum(nums[0, j)))f(i,j)=max(f(i−1,0),sum(nums[0,j)))
  • 将元素[1, j)划分为第i组,f(i,j)=max(f(i−1,1),sum(nums[1,j)))f(i, j) = max(f(i-1, 1), sum(nums[1, j)))f(i,j)=max(f(i−1,1),sum(nums[1,j)))
  • ...
  • 将元素[k, j)划分为第i组,f(i,j)=max(f(i−1,k),sum(nums[k,j)))f(i, j) = max(f(i-1, k), sum(nums[k, j)))f(i,j)=max(f(i−1,k),sum(nums[k,j)))

取这些集合的最小值就是f(i,j)f(i, j)f(i,j),写出方程

f(i,j)=min(max(f(i−1,k),preSum[j]−preSum[k])),k∈[0,j),j∈[i,n]f(i, j) = min(max(f(i-1,k),preSum[j]-preSum[k])) ,k\in[0,j),j\in[i,n] f(i,j)=min(max(f(i−1,k),preSum[j]−preSum[k])),k∈[0,j),j∈[i,n]

preSum[j]nums[0, j)的前缀和,n是nums的长度。

边界处理:

初始化二维dp数组为INF,dp[0][0]=0

  • 当i=1时,只有将所有的数划分在第i组才有效
  • 当i=2时,状态dp[i-1][j]j>=1是有效的

当不符合划分规则时,dp[i-1][j]=INF,i-1>j,也就无法选取这些不符合规则的划分。

代码语言:javascript
复制
class Solution {
    public int splitArray(int[] nums, int m) {
        int n = nums.length;
        int[] preSum = new int[n+1];
        int[][] dp = new int[m+1][n+1];
        for(int i = 1; i <= n; i++){
            preSum[i] += preSum[i-1] + nums[i-1];
        }

        for(int i = 0; i <= m; i++){
           Arrays.fill(dp[i], Integer.MAX_VALUE);
        }
        
        dp[0][0] = 0;
        for(int i = 1; i <= m; i++){
            for(int j = i; j <= n; j++){
                for(int k = 0; k < j; k++){
                    dp[i][j] = Math.min(dp[i][j], Math.max(dp[i-1][k], preSum[j] - preSum[k]));
                }
            }
        }

        return dp[m][n];
    }
}

复杂度分析

  • 时间复杂度:O(mn2)O(mn^2)O(mn2)
  • 空间复杂度:O(mn)O(mn)O(mn),m是组数,n是nums的长度。

DFS记忆化

用dfs去搜索任何可能划分,使用记忆化剪枝。

代码语言:javascript
复制
class Solution {
    // 前缀和数组,因为数组和会超出int范围,因此用long
    long[] s;
    int n;

    public int splitArray(int[] nums, int m) {
        n = nums.length;
        s = new long[n+1];
        for(int i = 1; i <= n; i++){
            s[i] = s[i-1] + nums[i-1];
        }

        return (int)helper(0, 0, m, new Long[n+1][m+1]);
    }

    // [i, n)是待划分的区域,j表示当前是第几个划分点,j个划分点能够划出j+1个区域
    public long helper(int i, int j, int m, Long[][] memo){
        if(memo[i][j] != null)
            return memo[i][j];
		
        // 当j+1等于m时,划分完毕,直接返回当前区域的和
        if(j+1 == m)
            return s[n] - s[i];

        long min = Integer.MAX_VALUE;
        // 穷尽所有划分点
        for(int k = i; k < n; k++){
            // t是当前划分的组内和的最大值
            long t = Math.max(s[k+1]-s[i], helper(k+1, j+1, m, memo));
            // min是所有可能划分的组内和的最大值的最小值
            min = Math.min(t, min);
        }

        // 记忆
        return memo[i][j] = min;
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/08/29 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 题目链接
  • 二分查找
    • 复杂度分析
    • 动态规划
      • 复杂度分析
      • DFS记忆化
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档