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.

Note:
Given m satisfies the following constraint: 1 ≤ m ≤ length(nums) ≤ 14,000.

Examples:

  1. Input:
  2. nums = [7,2,5,10,8]
  3. m = 2
  4.  
  5. Output:
  6. 18
  7.  
  8. Explanation:
  9. There are four ways to split nums into two subarrays.
  10. The best way is to split it into [7,2,5] and [10,8],
  11. where the largest sum among the two subarrays is only 18.

这道题给了我们一个非负数的数组 nums 和一个整数m,让把数组分割成m个非空的连续子数组,让最小化m个子数组中的最大值。开始以为要用博弈论中的最小最大化算法,可是想了半天发现并不会做,于是后面决定采用无脑暴力破解,在 nums 中取出所有的m个子数组的情况都找一遍最大值,为了加快求子数组和的运算,还建立了累计和数组,可以还是 TLE 了,所以博主就没有办法了,只能上网参考大神们的解法,发现大家普遍使用了二分搜索法来做,感觉特别巧妙,原来二分搜索法还能这么用,厉害了我的哥。首先来分析,如果m和数组 nums 的个数相等,那么每个数组都是一个子数组,所以返回 nums 中最大的数字即可,如果m为1,那么整个 nums 数组就是一个子数组,返回 nums 所有数字之和,所以对于其他有效的m值,返回的值必定在上面两个值之间,所以可以用二分搜索法来做。用一个例子来分析,nums = [1, 2, 3, 4, 5], m = 3,将 left 设为数组中的最大值5,right 设为数字之和 15,然后算出中间数为 10,接下来要做的是找出和最大且小于等于 10 的子数组的个数,[1, 2, 3, 4], [5],可以看到无法分为3组,说明 mid 偏大,所以让 right=mid,然后再次进行二分查找,算出 mid=7,再次找出和最大且小于等于7的子数组的个数,[1,2,3], [4], [5],成功的找出了三组,说明 mid 还可以进一步降低,让 right=mid,再次进行二分查找,算出 mid=6,再次找出和最大且小于等于6的子数组的个数,[1,2,3], [4], [5],成功的找出了三组,尝试着继续降低 mid,让 right=mid,再次进行二分查找,算出 mid=5,再次找出和最大且小于等于5的子数组的个数,[1,2], [3], [4], [5],发现有4组,此时的 mid 太小了,应该增大 mid,让 left=mid+1,此时 left=6,right=6,循环退出了,返回 right 即可,参见代码如下:

解法一:

  1. class Solution {
  2. public:
  3. int splitArray(vector<int>& nums, int m) {
  4. long left = , right = ;
  5. for (int i = ; i < nums.size(); ++i) {
  6. left = max(left, (long)nums[i]);
  7. right += nums[i];
  8. }
  9. while (left < right) {
  10. long long mid = left + (right - left) / ;
  11. if (can_split(nums, m, mid)) right = mid;
  12. else left = mid + ;
  13. }
  14. return right;
  15. }
  16. bool can_split(vector<int>& nums, long m, long sum) {
  17. long cnt = , curSum = ;
  18. for (int i = ; i < nums.size(); ++i) {
  19. curSum += nums[i];
  20. if (curSum > sum) {
  21. curSum = nums[i];
  22. ++cnt;
  23. if (cnt > m) return false;
  24. }
  25. }
  26. return true;
  27. }
  28. };

上面的解法相对来说比较难想,在热心网友 perthblank 的提醒下,再来看一种 DP 的解法,相对来说,这种方法应该更容易理解一些。建立一个二维数组 dp,其中 dp[i][j] 表示将数组中前j个数字分成i组所能得到的最小的各个子数组中最大值,初始化为整型最大值,如果无法分为i组,那么还是保持为整型最大值。为了能快速的算出子数组之和,还是要建立累计和数组,难点就是在于推导状态转移方程了。来分析一下,如果前j个数字要分成i组,那么i的范围是什么,由于只有j个数字,如果每个数字都是单独的一组,那么最多有j组;如果将整个数组看为一个整体,那么最少有1组,所以i的范围是[1, j],所以要遍历这中间所有的情况,假如中间任意一个位置k,dp[i-1][k] 表示数组中前k个数字分成 i-1 组所能得到的最小的各个子数组中最大值,而 sums[j]-sums[k] 就是后面的数字之和,取二者之间的较大值,然后和 dp[i][j] 原有值进行对比,更新 dp[i][j] 为二者之中的较小值,这样k在 [1, j] 的范围内扫过一遍,dp[i][j] 就能更新到最小值,最终返回 dp[m][n] 即可,博主认为这道题所用的思想应该是之前那道题 Reverse Pairs 中解法二中总结的分割重现关系 (Partition Recurrence Relation),由此看来很多问题的本质都是一样,但是披上华丽的外衣,难免会让人有些眼花缭乱了,参见代码如下:

解法二:

  1. class Solution {
  2. public:
  3. int splitArray(vector<int>& nums, int m) {
  4. int n = nums.size();
  5. vector<long> sums(n + );
  6. vector<vector<long>> dp(m + , vector<long>(n + , LONG_MAX));
  7. dp[][] = ;
  8. for (int i = ; i <= n; ++i) {
  9. sums[i] = sums[i - ] + nums[i - ];
  10. }
  11. for (int i = ; i <= m; ++i) {
  12. for (int j = ; j <= n; ++j) {
  13. for (int k = i - ; k < j; ++k) {
  14. long val = max(dp[i - ][k], sums[j] - sums[k]);
  15. dp[i][j] = min(dp[i][j], val);
  16. }
  17. }
  18. }
  19. return dp[m][n];
  20. }
  21. };

Github 同步地址:

https://github.com/grandyang/leetcode/issues/410

类似题目:

Reverse Pairs

参考资料:

https://leetcode.com/problems/split-array-largest-sum/

https://leetcode.com/problems/split-array-largest-sum/discuss/89816/DP-Java

https://leetcode.com/problems/split-array-largest-sum/discuss/89873/binary-search-c-solution

https://leetcode.com/problems/split-array-largest-sum/discuss/89817/Clear-Explanation%3A-8ms-Binary-Search-Java

LeetCode All in One 题目讲解汇总(持续更新中...)

[LeetCode] Split Array Largest Sum 分割数组的最大值的更多相关文章

  1. [LeetCode] 410. Split Array Largest Sum 分割数组的最大值

    Given an array which consists of non-negative integers and an integer m, you can split the array int ...

  2. 410 Split Array Largest Sum 分割数组的最大值

    给定一个非负整数数组和一个整数 m,你需要将这个数组分成 m 个非空的连续子数组.设计一个算法使得这 m 个子数组各自和的最大值最小.注意:数组长度 n 满足以下条件:    1 ≤ n ≤ 1000 ...

  3. Leetcode: Split Array Largest Sum

    Given an array which consists of non-negative integers and an integer m, you can split the array int ...

  4. 410. Split Array Largest Sum 把数组划分为m组,怎样使最大和最小

    [抄题]: Given an array which consists of non-negative integers and an integer m, you can split the arr ...

  5. [Swift]LeetCode410. 分割数组的最大值 | Split Array Largest Sum

    Given an array which consists of non-negative integers and an integer m, you can split the array int ...

  6. 【leetcode】410. Split Array Largest Sum

    题目如下: Given an array which consists of non-negative integers and an integer m, you can split the arr ...

  7. Split Array Largest Sum

    Given an array which consists of non-negative integers and an integer m, you can split the array int ...

  8. 动态规划——Split Array Largest Sum

    题意大概就是,给定一个包含非负整数的序列nums以及一个整数m,要求把序列nums分成m份,并且要让这m个子序列各自的和的最大值最小(minimize the largest sum among th ...

  9. 410. Split Array Largest Sum

    做了Zenefits的OA,比面经里的简单多了..害我担心好久 阴险的Baidu啊,完全没想到用二分,一开始感觉要用DP,类似于极小极大值的做法. 然后看了答案也写了他妈好久. 思路是再不看M的情况下 ...

随机推荐

  1. ASP.NET MVC 登录验证

     好久没写随笔了,这段时间没 什么事情,领导 一直没安排任务,索性 一直在研究代码,说实在的,这个登录都 搞得我云里雾里的,所以这次我可能也讲得不是 特别清楚,但是 我尽力把我知道的讲出来,顺便也对自 ...

  2. ASP.NET Core 中文文档 第四章 MVC(2.2)模型验证

    原文:Model Validation 作者:Rachel Appel 翻译:娄宇(Lyrics) 校对:孟帅洋(书缘) 在这篇文章中: 章节: 介绍模型验证 验证 Attribute 模型状态 处理 ...

  3. linux iptables常用命令之配置生产环境iptables及优化

    在了解iptables的详细原理之前,我们先来看下如何使用iptables,以终为始,有可能会让你对iptables了解更深 所以接下来我们以配置一个生产环境下的iptables为例来讲讲它的常用命令 ...

  4. Basic Tutorials of Redis(8) -Transaction

    Data play an important part in our project,how can we ensure correctness of the data and prevent the ...

  5. fastjson-alibaba

    fastjson使用 package com.alibaba.json.demo; import org.junit.Assert; import com.alibaba.fastjson.JSON; ...

  6. MySql Access denied for user 'root'@'localhost' (using password:YES) 解决方案

    关于昨天下午说的MySQL服务无法启动的问题,解决之后没有进入数据库,就直接关闭了电脑. 今早打开电脑,开始-运行 输入"mysql -uroot -pmyadmin"后出现以下错 ...

  7. eclipse SE增加Web开发插件

    最近接触了些java项目,之前安装了eclipse SE版本.没有Web开发插件,调试不了Web代码.点击“Window”--“Preference” 左边菜单栏是找不到“Server”项来配置服务器 ...

  8. LDAP注入与防御解析

    [目录] 0x1 LDAP介绍 0x2 LDAP注入攻击及防御 0x3 参考资料 0x1 LDAP介绍 1 LDAP出现的背景 LDAP(Lightweight Directory Access Pr ...

  9. SharePoint 2013 Designer 入门教程

    SharePoint的使用中,SharePoint Designer是非常重要的工具,我们可以通过Designer设计页面.母版页,维护.管理站点,也可以定制列表表单.数据视图,设计工作流等等.下面总 ...

  10. listview侧滑删除

    自定义Listview,向左滑动,右边刚好显示删除按钮: public class SlideListView extends ListView { private int mScreenWidth; ...