Time: 20190904
Type: Hard, DP

题目描述

给定一个非负整数数组和一个整数 m,你需要将这个数组分成 m 个非空的连续子数组。设计一个算法使得这 m 个子数组各自和的最大值最小。

注意:
数组长度 n 满足以下条件:

1 ≤ n ≤ 1000
1 ≤ m ≤ min(50, n)
示例:

输入:
nums = [7,2,5,10,8]
m = 2

输出:
18

解释:
一共有四种方法将nums分割为2个子数组。
其中最好的方式是将其分为[7,2,5] 和 [10,8],
因为此时这两个子数组各自的和的最大值为18,在所有情况中最小。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/split-array-largest-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

关于DP类问题,个人感觉需要先在纸上推导明白状态定义,递推公式,能手工推导一部分解,然后有了大概的概念就可以转换为代码了。

这是一类最大最小问题。

简单说就是将数组划分为多组,每组会求一个和,多组的和最大值的可能性很多,求多组和的最小值的可能性。

状态定义:f[i][j]表示nums[0] ~ nums[j]j+1个元素划分为i组的和的最大最小值。
可初始化的状态:f[1][j]表示nums[0]~nums[j]划分为1组的分组和的最大最小值,显然f[1][j] = sum(0, j),包含边界。
状态迁移方程:f[i][j] = min(max(f[i-1][k], sum(k+1, j))), 0<= k < j

这种问题的状态迁移方程很具有代表性,即在右边划一段出来,先把问题规模-1,然后剩下的只需要再切分为i-1组即可。

其中k是这缩小问题规模的切分点。

代码

class Solution:def splitArray(self, nums: List[int], m: int) -> int:presum = [0 for i in range(len(nums))]presum[0] = nums[0]for i in range(1, len(nums)):presum[i] =  presum[i-1]  + nums[i]    # presum[j] - presum[i] = [i+1, j]# f[i][j]表示从nums[0]~nums[j]分成i组的解f = [[float('inf')] * len(nums) for i in range(m+1)]# f[1][j] = sum(0, j), 包含边界,表示只划分一段时的解for j in range(len(nums)):f[1][j] = presum[j]for i in range(2, m + 1):for j in range(i - 1, len(nums)):for k in range(0, j):f[i][j] = min(f[i][j], max(f[i-1][k], presum[j] - presum[k]))return f[m][len(nums)-1]

但是这道题用Python会超时,用C++不会超时。

class Solution {
public:int splitArray(vector<int>& nums, int m) {int n = nums.size();vector<vector<long>> f(n + 1, vector<long>(m + 1, INT_MAX));vector<long> sub(n + 1, 0);for (int i = 0; i < n; i++) {sub[i + 1] = sub[i] + nums[i];}f[0][0] = 0;for (int i = 1; i <= n; i++) {for (int j = 1; j <= m; j++) {for (int k = 0; k < i; k++) {f[i][j] = min(f[i][j], max(f[k][j - 1], sub[i] - sub[k]));}}}return f[n][m];}
};

时空复杂度

时间复杂度:O(mn2)O(mn^2)O(mn2)
空间复杂度:O(mn)O(mn)O(mn)

本题最优解是二分法,时间复杂度要比这个要优秀得多。

下面就来看二分法是什么思路以及如何写出对应的代码。

二分法的思路

本题二分法的思路很有意思,首先我们想子数组的最大和一定比所有元素的最大值要大或者相等,要比整个数组的和要小。

有了上下界,我们就可以用二分的方式在其中遍历。遍历的过程是,我们从上下界的中间出发,定一个最大和的值mid,然后对数组进行划分,从左往右,加和恰好大于mid时,表示当前这个num不能再往本组加了,一加就超过了,所以一组划分好了,我们用temp跟踪当前的数组和,既然这个num不属于当前一组,一定是下一组,于是temp = num。在temp没大于mid前,只管累加即可。

统计出当前mid下可以划分多少组后,就可以和m比较,如果划分的组数大于m,表示mid值太小了,将下边界left值变大,否则划分的组数没到m,表示mid大了,将上边界right下移。

二分的骨架是一样的,重点是这个思路以及在mid的统治下统计有多少组。

理解了这个二分,代码将变得非常简单自然。


class Solution:def splitArray(self, nums: List[int], m: int) -> int:def countGroups(mid):temp = 0count = 1for num in nums:temp += numif temp > mid:count += 1temp = num # 准备下一组return countleft, right = max(nums), sum(nums)while left < right:mid = left + (right - left) // 2num_group = countGroups(mid)if num_group > m: # 划分多了,mid太小了left = mid + 1else:right = midprint(left, mid, right)return left # left恰好是满足条件的最少分割,自然就最大

注意count从1开始计数,因为无论如何都有一组。

END.

Leetcode 410.分割数组的最大值(最优解是二分法)相关推荐

  1. leetcode 410. 分割数组的最大值(二分法)

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

  2. LeetCode 410——分割数组的最大值

    1. 题目 2. 解答 此题目为 今日头条 2018 AI Camp 5 月 26 日在线笔试编程题第二道--最小分割分数. class Solution {public:// 若分割数组的最大值为 ...

  3. LeetCode 410. 分割数组的最大值(极小极大化 二分查找 / DP)

    文章目录 1. 题目 2. 解题 2.1 二分查找 2.2 DP 1. 题目 给定一个非负整数数组和一个整数 m,你需要将这个数组分成 m 个非空的连续子数组. 设计一个算法使得这 m 个子数组各自和 ...

  4. LeetCode 410. 分割数组的最大值

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

  5. 410. 分割数组的最大值

    题解: 链接:https://leetcode-cn.com/problems/split-array-largest-sum/ 下面这种情况是dfs是超时的,有时间加一下记忆化 class Solu ...

  6. 四边形不等式技巧——分割数组的最大值(画家问题)

    题目 给定一个整型数组arr,数组中的每个值都为正数,表示完成一幅画作需要的时间,再给定一个整数num,表示画匠的数量,每个画匠只能画连在一起的画作.所有的画家并行工作,请返回完成所有的画作需要的最少 ...

  7. 7、leetcode410 分割数组的最大值

    leetcode410 分割数组的最大值 给定一个非负整数数组 nums 和一个整数 m ,你需要将这个数组分成 m 个非空的连续子数组. 设计一个算法使得这 m 个子数组各自和的最大值最小. 示例 ...

  8. LeetCode 659. 分割数组为连续子序列

    LeetCode 659. 分割数组为连续子序列 回顾 根据题意,每个元素只会涉及两种情况: 已经存在nums[i]-1结尾的序列,我们把nums[i]接到之前的某个序列后. 尝试以nums[i]为头 ...

  9. 分割数组的最大值—leetcode410

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

最新文章

  1. SpringCloud + Consul服务注册中心 + gateway网关
  2. SQL查询语句大全集锦(一)
  3. centos7下安装gcc7
  4. linux c嵌入汇编语言,Linux 下的C和Intel 汇编语言混用
  5. [bzoj4625][BeiJing2016]水晶
  6. java sorted_Java记录 -59- SortedSet
  7. ssas脚本组织程序_脚本调试编辑
  8. (转)Vue 爬坑之路(四)—— 与 Vuex 的第一次接触
  9. android多图片拖动,Android实现图片拖动效果
  10. apache、nignx等日志分析工具
  11. vue可视化拖拽生成工具_GitHub - 1260215278/dragUI: 基于vuedraggable.js + uni 的可视化拖拽编程,自动生成项目,自动生成代码,自行导入第三方组件...
  12. 小猿圈分享-数据分析工具
  13. windows下使用iconv命令批量原地转码文件
  14. 他 25 岁进贝尔实验室,32 岁提信息论,40 岁办达特茅斯会议,晚年患上阿兹海默 | 人物志...
  15. 高速公路匝道口事故何时了?
  16. 建筑企业收并购的三要素
  17. Effective C++ NVI手法
  18. UniApp文件上传
  19. Java ZIP压缩 ZipArchiveEntry实现ZIP高效、Java多线程压缩、可控CPU使用率 Apache commons-compress
  20. FRED应用: LED混合准直透镜模拟

热门文章

  1. HTML知识积累及实践(六) - pre,混合框架
  2. Linux搭建NFS文件服务器
  3. Hexo+next的侧边栏背景与字体颜色设置方法
  4. 《游戏之旅-我的编程感悟》读书笔记
  5. centos7安装rabbitmq_rabbitmq v3.7.16安装部署文档
  6. zabbix 5.0所有依赖包_一杯茶的时间,上手Zabbix
  7. python工具的功能介绍_Python功能工具
  8. Android Google Map –两点之间的绘图路线
  9. js 内存引用计数_快速内存管理–自动引用计数
  10. 具有Eclipse和嵌入式JBoss HornetQ Server的简单JMS 1.1生产者和使用者示例