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:

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.

这道题给了我们一个非负数的数组 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 即可,参见代码如下:

解法一:

class Solution {
public:
int splitArray(vector<int>& nums, int m) {
long left = , right = ;
for (int i = ; i < nums.size(); ++i) {
left = max(left, (long)nums[i]);
right += nums[i];
}
while (left < right) {
long long mid = left + (right - left) / ;
if (can_split(nums, m, mid)) right = mid;
else left = mid + ;
}
return right;
}
bool can_split(vector<int>& nums, long m, long sum) {
long cnt = , curSum = ;
for (int i = ; i < nums.size(); ++i) {
curSum += nums[i];
if (curSum > sum) {
curSum = nums[i];
++cnt;
if (cnt > m) return false;
}
}
return true;
}
};

上面的解法相对来说比较难想,在热心网友 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),由此看来很多问题的本质都是一样,但是披上华丽的外衣,难免会让人有些眼花缭乱了,参见代码如下:

解法二:

class Solution {
public:
int splitArray(vector<int>& nums, int m) {
int n = nums.size();
vector<long> sums(n + );
vector<vector<long>> dp(m + , vector<long>(n + , LONG_MAX));
dp[][] = ;
for (int i = ; i <= n; ++i) {
sums[i] = sums[i - ] + nums[i - ];
}
for (int i = ; i <= m; ++i) {
for (int j = ; j <= n; ++j) {
for (int k = i - ; k < j; ++k) {
long val = max(dp[i - ][k], sums[j] - sums[k]);
dp[i][j] = min(dp[i][j], val);
}
}
}
return dp[m][n];
}
};

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. ORA 各种oraclesql错误

    ORA-00001: 违反唯一约束条件 (.) ORA-00017: 请求会话以设置跟踪事件 ORA-00018: 超出最大会话数 ORA-00019: 超出最大会话许可数 ORA-00020: 超出 ...

  2. C# 本质论 第四章 方法和参数

    要为方法名使用动词或动词短语 递归:递归调用方法 方法重载: try catch

  3. C#开发微信门户及应用(34)--微信裂变红包

    在上篇随笔<C#开发微信门户及应用(33)--微信现金红包的封装及使用>介绍了普通现金红包的封装和使用,这种红包只能单独一次发给一个人,用户获取了红包就完成了,如果我们让用户收到红包后,可 ...

  4. 【Java每日一题】20161223

    package Dec2016; public class Ques1223 { public static void main(String[] args){ Integer obj = Integ ...

  5. redis 安装

    安装wget http://download.redis.io/releases/redis-3.2.3.tar.gztar -xzvf redis-3.2.3.tar.gzcd redis-3.2. ...

  6. 《连载 | 物联网框架ServerSuperIO教程》-4.如开发一套设备驱动,同时支持串口和网络通讯。附:将来支持Windows 10 IOT

    1.C#跨平台物联网通讯框架ServerSuperIO(SSIO)介绍 <连载 | 物联网框架ServerSuperIO教程>1.4种通讯模式机制. <连载 | 物联网框架Serve ...

  7. [连载]《C#通讯(串口和网络)框架的设计与实现》- 13.中英文版本切换设计

    目       录 第十三章     中英文版本切换设计... 2 13.1        不用自带的资源文件的理由... 2 13.2        配置文件... 2 13.3        语言 ...

  8. 高访问量WEB开发中的架构模式,学习从点滴开始

     当一个Web系统从日访问量10万逐步增长到1000万,甚至超过1亿的过程中,Web系统承受的压力会越来越大,在这个过程中,我们会遇到很多的问题.为了解决这些性能压力带来问题,我们需要在Web系统架构 ...

  9. MvcPager使用的Demo(同步分页)

    最近接触了一下MvcPager,昂...来做个笔记吧 其实,我喜欢前后端分离,分页这种东西前端负责的地方,后端不用顾问,这里的MvcPager有点让我想起服务器控件,毕竟用到了HtmlHelper. ...

  10. [Android]Android MVP&依赖注入&单元测试

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5422443.html Android MVP&依赖注入 ...