[LeetCode] 410. Split Array Largest Sum 分割数组的最大值
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
类似题目:
参考资料:
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
LeetCode All in One 题目讲解汇总(持续更新中...)
[LeetCode] 410. Split Array Largest Sum 分割数组的最大值的更多相关文章
- 410 Split Array Largest Sum 分割数组的最大值
给定一个非负整数数组和一个整数 m,你需要将这个数组分成 m 个非空的连续子数组.设计一个算法使得这 m 个子数组各自和的最大值最小.注意:数组长度 n 满足以下条件: 1 ≤ n ≤ 1000 ...
- [LeetCode] Split Array Largest Sum 分割数组的最大值
Given an array which consists of non-negative integers and an integer m, you can split the array int ...
- 410. Split Array Largest Sum 把数组划分为m组,怎样使最大和最小
[抄题]: Given an array which consists of non-negative integers and an integer m, you can split the arr ...
- 【leetcode】410. Split Array Largest Sum
题目如下: Given an array which consists of non-negative integers and an integer m, you can split the arr ...
- 410. Split Array Largest Sum
做了Zenefits的OA,比面经里的简单多了..害我担心好久 阴险的Baidu啊,完全没想到用二分,一开始感觉要用DP,类似于极小极大值的做法. 然后看了答案也写了他妈好久. 思路是再不看M的情况下 ...
- [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 ...
- Leetcode: Split Array Largest Sum
Given an array which consists of non-negative integers and an integer m, you can split the array int ...
- Split Array Largest Sum
Given an array which consists of non-negative integers and an integer m, you can split the array int ...
- 动态规划——Split Array Largest Sum
题意大概就是,给定一个包含非负整数的序列nums以及一个整数m,要求把序列nums分成m份,并且要让这m个子序列各自的和的最大值最小(minimize the largest sum among th ...
随机推荐
- promise处理回调地狱
promise 异步调用 异步结果分析 定时任务 ajax 自定义事件函数 多次异步调用依赖分析(promise 应用场景) 多次异步调用结果顺序不确定问题 $.ajax({ url: 'http:l ...
- 【前端知识体系-JS相关】深入理解JavaScript异步和单线程
1. 为什么JavaScript是单线程? JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事.那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊. Jav ...
- Linux chattr 文件保护
Linux chattr 文件保护 chattr命令的用法:chattr [ -RV ] [ -v version ] [ mode ] files…注:最关键的是在[mode]部分,[mode]部分 ...
- C++中Lambda表达式转化为函数指针
// ----------------------------------------------------------- auto combineCallbackLambda = [](GLdou ...
- JMeter性能测试入门--简单使用
1.JMeter整体简介 Apache JMeter是Apache组织开发的基于Java的压力测试工具.用于对软件做压力测试,它最初被设计用于Web应用测试,但后来扩展到其他测试领域. 它可以用于测试 ...
- Vue计算属性computed的全面解析
前言 一直以来对computed这个计算属性都只停在一个大概的认知中,最近特意仔细研读相关资料,亲测后逐渐了解了其特性. 正文 computed 特点: 1.初始化/依赖属性(即data属性)改变时执 ...
- Mysql Join-连接查询(上)
认识 多表连接查询,我感觉应该是关系型数据库最能体现其价值和灵活性的地方吧. 我觉得数据库的作用, 归纳起来无非就是存储和查询. 一言蔽之,数据库就是能灵活地存储和查询数据. 存储上, 也是以文件的方 ...
- win10无法在桌面右键快捷打开个性化设置、显示设置,在任务栏右键无法快捷打开任务栏设置
Win+R,输入regedit,定位到HKEY_CURRENT_USER\Software\Classes\ 找到列表中的ms-settings 将其删除即可解决! 转自百度知道
- 使用ES对中文文章进行分词,并进行词频统计排序
前言:首先有这样一个需求,需要统计一篇10000字的文章,需要统计里面哪些词出现的频率比较高,这里面比较重要的是如何对文章中的一段话进行分词,例如“北京是×××的首都”,“北京”,“×××”,“中华” ...
- 模板渲染 templates
目录 一.模板含义 二.模板的组成 三.逻辑控制代码 变量 标签 自定义过滤器 模板继承 一.模板含义 模板虽然是HTML文件,但是Django不是直接把HTML文件返回给用户,而是经过了 模板语言的 ...