今天再来一道hard模式的题目!
题目描述:
给定一个非负整数数组和一个整数 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,在所有情况中最小。
题目分析:
初一看这道题目似乎无从下手,但是还记得算法哥前一次分享的文章吗?没错,这个题目和前一次的题目解题思路是一致的,二分所要找的答案!显然,答案的范围是[0, nums数组和]之间的。
假设此时我们猜测一个答案mid,如果此时可以把数组拆分成m个非空连续子数组,使得每个子数组的和都小于等于mid,那么mid可能是我们要找的答案,而且,最优的那个答案必然是小于等于mid的(因为此时mid是一个解了,难道我们还去找一个比mid大的解吗 ?);
如果此时的mid值太小,导致拆分后,子数组的个数大于m,说明我们要找的最优解必然是大于mid值的;
因此,我们可以再次使用和前一天分享的题目那样的做法,二分枚举要找的最优解,然后验证是否可以使得数组拆分成符合条件的m个连续子数组,验证就非常简单,每次我使得子数组的和尽可能接近mid(因为每个子数组的和越靠近mid,就使得拆分后子数组的数量尽可能少,子数组数量越小,说明最优解需要继续减小枚举的范围),这一段理解起来会有点绕,结合前面的分析多读几遍,算法哥给出的源码相信你能看懂!
源码
复杂度分析:
二分答案的复杂度是log级别的,假设答案是int64的,那么log之后最大也就64左右,而每次验证是O(n)的,所以我们的复杂度是O(n)的,击败leetcode上100%的提交!
程序运行时间分析
总结:
今天这个题目,我们再一次看到了二分思想再解决算法难道上的妙用,很多初学者对二分始终停留在二分查找的概念里,今天的题目你有收获吗?