首页 > 其他分享 >【LeetCode 410】分割数组的最大值

【LeetCode 410】分割数组的最大值

时间:2024-05-12 17:09:36浏览次数:36  
标签:nums int 最大值 mid num 410 数组 LeetCode dp

题目描述

原题链接: LeetCode.410 分割数组的最大值

解题思路

  • 分割的是连续子数组, 所以模拟分割求子数组之和可以利用前缀和计算;
  • 设前缀和数组preSume[i]含义为[0,..,i]范围元素之和, 某个子数组subArray[i, j]的元素和就是preSum[j] - preSum[i];
  • 求K个子数组元素和的最大值能转换为前K-1个子数组和的最小值与第K个子数组和之间的较小值, 能转化为更小规模问题就可以考虑用动态规划来解决问题了:
    1. 递推数组dp[i][j]定义为[0,..,i]范围元素划分为j+1个子数组的元素和最小值, 其它初始赋值为最大值;
    2. 初始化dp[i][0]的值就是前缀和preSum[i]的值;
    3. 前m-1个子数组总共至少要有m-1个元素才能划分出来, 所以第K个子数组要从[m-1, length - 1]范围枚举来递推, 即 \(dp[i][m] = Math.min(dp[i][m], Math.max(dp[j][m - 1], preSum[j] - preSum[i])), j \in [m - 1, length)\)
    4. 时间复杂度O(k\(n^2\)), 空间复杂度O(kn);
  • 这种求某些最大值的最小答案问题还可以考虑二分查找法, 一般也是最优解法:
    1. 首先确定答案可能的范围, 显然每个元素单独作为一个元素时对应的整个原始数组元素最大值是答案下限, 所有元素作为一个数组的原始数组累加和是答案上限, 即left = max, right = sum;
    2. 再来确定单调性, 假定按照某个临界值来划分子数组的数量超过了K, 说明临界值设的太小需要往较大的范围尝试, 反之则说明一定能划分出不超过该临界值的K个连续子数组;
    3. 判断函数的逻辑也可以直接确定, 即给定子数组元素和不超过mid的情况下, 能否划分出不超过K个的子数组;
    4. 在[max, sum]范围内不断二分求得能划分出不超过K个子数组的mid最小值就是答案;
    5. 设数组和为S, 时间复杂度O(nlogS), 空间复杂度O(1);

解题代码

  • 动态规划版本

      /**
       * 动态规划
       * 执行用时: 84 ms , 在所有 Java 提交中击败了 18.46% 的用户
       * 内存消耗: 40.71 MB , 在所有 Java 提交中击败了 14.80% 的用户
       */
      public int splitArray(int[] nums, int k) {
          // dp[i][j]表示[0,i]分为j+1个子数组的元素和最大值中的最小值
          int len = nums.length;
          int[][] dp = new int[len][k];
          dp[0][0] = nums[0];
          for (int i = 1; i < len; i++) {
              dp[i][0] = dp[i - 1][0] + nums[i];
          }
          for (int i = 0; i < len; i++) {
              for (int j = 1; j < k; j++) {
                  dp[i][j] = Integer.MAX_VALUE;
              }
          }
          for (int m = 1; m < k; m++) {
              // m+1个子数组至少需要[0,m]共m+1个元素才能划分出来
              for (int i = m; i < len; i++) {
                  // 以[0,j]划分m个子数组, [j+1,i]为第m+1个子数组, 依次求j=m-1 ~ j=i进行划分的情况下各个子数组最大值的最小值
                  for (int j = m - 1; j < i; j++) {
                      dp[i][m] = Math.min(dp[i][m], Math.max(dp[j][m - 1], dp[i][0] - dp[j][0]));
                  }
              }
          }
          return dp[len - 1][k - 1];
      }
    
  • 个人喜好用的二分查找写法:

      /**
       * 用可读性更高的另一种二分写法, 用来水篇博客
       * 执行用时: 0 ms , 在所有 Java 提交中击败了 100.00% 的用户
       * 内存消耗: 40.25 MB , 在所有 Java 提交中击败了 64.60% 的用户
       */
      public int splitArray(int[] nums, int k) {
          int left = 0, right = 0;
          for (int num : nums) {
              right += num;
              left = Math.max(num, left);
          }
          int ans = -1;
          while (left <= right) {
              int mid = left + ((right - left) >> 1);
              // 判断数组和都不超过mid的情况下能不能分成K个连续子数组
              if (splitMoreThanK(nums, mid, k)) {
                  left = mid + 1;
              }
              else {
                  ans = mid;
                  right = mid - 1;
              }
          }
          return ans;
      }
    
      private boolean splitMoreThanK(int[] nums, int sumLimit, int k) {
          int cnt = 1, s = 0;
          for (int num : nums) {
              if (s + num > sumLimit) {
                  cnt++;
                  s = num;
                  if (cnt > k) {
                      break;
                  }
              }
              else {
                  s += num;
              }
          }
          return cnt > k;
      }
    
  • 更简洁些的二分写法:

      /**
       * 二分查找
       * 执行用时: 0 ms , 在所有 Java 提交中击败了 100.00% 的用户
       * 内存消耗: 40.09 MB , 在所有 Java 提交中击败了 95.39% 的用户
       */
      public int splitArrayBinarySearch(int[] nums, int k) {
          int max = 0, sum = 0;
          for (int num : nums) {
              max = Math.max(max, num);
              sum += num;
          }
          // 子数组元素和最大值的最小数范围在[max, sum]之内
          int small = max, big = sum;
          while (small < big) {// 二分找到符合条件的最小值
              int mid = small + (big - small) / 2;
              int cnt = splitCnt(nums, mid);
              if (cnt > k) {
                  // 划分的子数组个数超过了k, 说明当前目标值小了需要扩大
                  small = mid + 1;
              }
              else {
                  // 缩小目标值尝试找到能满足元素和最大值的最小情况
                  big = mid;
              }
          }
          return small;
      }
    
      /**
       * @param sumLimit 划分连续子数组的元素和最大值限制
       * @return 满足元素和不超过指定值条件下的子数组个数
       */
      private int splitCnt(int[] nums, int sumLimit) {
          int cnt = 1, sum = 0;
          for (int num : nums) {
              if (sum + num > sumLimit) {// 当前连续子数组元素和已经超过限制, 要划分出新的子数组
                  sum = 0;
                  cnt++;
              }
              sum += num;
          }
          return cnt;
      }
    

标签:nums,int,最大值,mid,num,410,数组,LeetCode,dp
From: https://www.cnblogs.com/coding-memory/p/18187954

相关文章

  • 【LeetCode 162】寻找峰值
    题目描述原题链接:LeetCode.162寻找峰值解题思路数组相邻元素不相等,峰值可能有多个,整个数组的最大值肯定是峰值之一,直接遍历数组获取最大值也能得到答案;但是写明复杂度要求O(logn)就是否定了最简单的O(n)遍历解法,需要用二分法;按照题意数组边界另一端等同于无穷小,可......
  • MSB410在条件(%(Name)' == InputFile' OR %(Name)' == OutputFile
    问题:在VS2017中配置完了Qtvisualstudiotools插件后,编译时报错< MSB410在条件(%(Name)'==InputFile'OR%(Name)'==OutputFile > 原因及解决方法:插件版本问题,先卸载最新版,下载旧版:https://download.qt.io/archive/vsaddin/  此版本报错 安装后断网,取消自动更......
  • 669. 修剪二叉搜索树(leetcode)
    https://leetcode.cn/problems/trim-a-binary-search-tree/description/要点是区分在区间左边还是右边,在区间左边那么右子树也还有必要去查找删除,右边同理,返回的是删除后新树的根节点要注意函数要实现单层逻辑和完成闭环语义classSolution{//查找要删除的节点,进行......
  • 450. 删除二叉搜索树中的节点(leetcode)
    https://leetcode.cn/problems/delete-node-in-a-bst/要点是确定函数语义,单层递归逻辑,确定好有返回值的这种写法,分析出5种情况,递归的时候接收好递归的返回的新树的根节点即可classSolution{//函数语义(单层递归逻辑):查找要删除的节点,并且返回删除节点后新的树的......
  • [LeetCode] 最短的桥 双BFS Java
    Problem:934.最短的桥目录思路复杂度Code思路先找到第一个岛屿,根据每一个岛屿的岛屿块的位置多源查找这个块与第二个岛屿的距离,先找到的就是最少的距离同时,将已遍历过的岛屿标记为-1,避免重复入队复杂度时间复杂度:添加时间复杂度,示例:$O(n^2)$空间复杂度:添......
  • LeetCode 1186. Maximum Subarray Sum with One Deletion
    原题链接在这里:https://leetcode.com/problems/maximum-subarray-sum-with-one-deletion/description/题目:Givenanarrayofintegers,returnthemaximumsumfora non-empty subarray(contiguouselements)withatmostoneelementdeletion. Inotherwords,youwa......
  • LeetCode 2210. Count Hills and Valleys in an Array
    原题链接在这里:https://leetcode.com/problems/count-hills-and-valleys-in-an-array/description/题目:Youaregivena 0-indexed integerarray nums.Anindex i ispartofa hill in nums iftheclosestnon-equalneighborsof i aresmallerthan nums[i].......
  • 235. 二叉搜索树的最近公共祖先(leetcode)
    https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-search-tree/要点是如果root是在p和q之间的值,意味着已经找到了最近公共祖先/***Definitionforabinarytreenode.*structTreeNode{*intval;*TreeNode*left;*TreeNode*rig......
  • 236. 二叉树的最近公共祖先(leetcode)
    https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/description/要点是后序遍历,这样就可以从树底开始搜索,寻找最近公共祖先classSolution{public://返回的是最近公共祖先,若返回null则说明没有TreeNode*lowestCommonAncestor(TreeNode*r......
  • leetCode 001.两数之和
    给定一个整数数组nums和一个整数目标值target,请你在该数组中找出和为目标值target的那两个整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。你可以按任意顺序返回答案。示例1:输入:nums=[2,7,11,15],ta......