题解 Leetcode LCP.14 切分数组
题目
给定一个整数数组 nums
,小李想将 nums
切割成若干个非空子数组,使得每个子数组最左边的数和最右边的数的最大公约数大于 1 。为了减少他的工作量,请求出最少可以切成多少个子数组。
示例 1:
输入:nums = [2,3,3,2,3,3]
输出:2
解释:最优切割为 [2,3,3,2] 和 [3,3] 。第一个子数组头尾数字的最大公约数为 2 ,第二个子数组头尾数字的最大公约数为 3 。
示例 2:
输入:nums = [2,3,5,7]
输出:4
解释:只有一种可行的切割:[2], [3], [5], [7]
限制:
1 <= nums.length <= 10^5 2 <= nums[i] <= 10^6 通过次数 1,872 提交次数 10,478
来源:力扣(LeetCode)链接:https://leetcode-cn.com/problems/qie-fen-shu-zu 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
注意到nums.length>=1
,所以从长度为 1 起分析,记L=nums.length
,记答案为ANS
,记a&b=gcd(a, b)>1
,其中gcd
即为求最大公约数的函数。
- 当
L==1
时,显然ANS=1
。 - 当
L==2
时,记该串为ab
,则ANS = a&b ? 1 : 2
。 - 现在分析
L==3
时的情况。当L==3
时,记串为abc
,若a&b
,则ANS=1
;否则若a&b || b&c
,则ANS=2
;否则,ANS=3
。
为一般化,我们记该串为,记为该串能切分的最小子数组个数。
显然,我们需要找出一个递推关系(减而治之)或者归并关系(分而治之)。
首先探求与之间是否存在关系。
- 当时,我们很容易得到,因为无论等于 1 还是其它, 它都可以写成多个子数组的形式,其中最后一个子数组的末尾就是,也即和最后一个子数组的第一个数(或者它本身)满足非互质的条件。于是同样可以得到这个数与依旧非互质,不会改变原先的结果。所以。
- 难的是的场景。最简单的可能是,于是这个串的总长结果就为 1,皆大欢喜。但我们必须着手分析当的情况。此时,也有最简单的一种可能,即与左边个数全部互质,显然,此时。尽管这一点的得出是很容易的,但却仿佛蕴含着什么不可告人的秘密。
- 现在假设不是全互质,而是有一个互质,此时我们不妨令串为,即和是非互质的,很显然,它们可以合成一个子数组,而一旦合并成子数组,从到中的这一串就等于完全消掉了,于是,这是显然的,同时也是显然的。这就是一个很有用的递推关系了,我们待会还要用到,但要注意此时我们尚未能够知道和之间的关系。
- 刚刚只是推导出了一个上限,我们还得知道下限。当我们拿到的时候,如果我们什么也不做(即独立成组),则导致增一,与目标需求是不合的。因此我们必然需要往前搜索,尝试将与某个数组合成一组,看看能不能将整体目标值降下来。但和谁组呢?一个自然的想法是和组,即与和自己非互质的数组成一组后,会变成的结果,如果恰好的话,答案就是 2 了,肯定没有比这个更优的结果了,因为和互质,否则答案直接变成 1 了。
- 但是必然要和自己非互质的数(设为)一组吗?这是一个听起来有点荒诞的问题,难道还能有其他选择吗?但我们或许可以给它一次机会,看看它能兴出什么风,作出什么浪来。我们假设它去选择和一组,这样的话,就把原数组分成两个子数组,左边是到的左边,右边是到,此时能够让整体更优吗?但是它既然想这么干的话,肯定有它自己的想法,我们估摸着猜一下,当它觉从处一刀切下来后,我们顺势就势,令原先的构成就为到的左边,这样左边一部分能切割出的子数组个数就相等了。然后看右边,右边原先是到的左边,这边假设原先最少能够分成个数组,现在多了一个,让这个子数组的个数能够减下来,那显然是因为这一串里面有个,它能和有机结合,于是让这个子数组的个数变成到的左边再加上 1,于是我们立即可以知道,如果是这样的话,为什么我们不直接在的左边来一刀呢?(也就是和自己非互质的数为一组)这样能够保证子数组个数不会大于在任意一个数上来一刀。
- 基于以上的分析,我们知道了,每当加入一个后,新的整体最优方案,必然从每个与非互质的处产生,只需在这儿来上一刀,整体就能变成,例如左边就是与非互质的数,则答案立即锐减到了 2,而除去此种办法,别无其他更好的办法有效降低最终子数组的个数了。这是解决本题最关键的一点:分而治之的点必然在与之前非互质的点中产生,于是结果就是。
- 基于第 6 点我们已经能够写出复杂度的算法,每个点找其所有的“前缀”(前面 k 个),求其 k 个子串中所能得到的最小子数组个数,然后加一。但是对于 AC 本题还是不够的。我们需要进一步分析。事实上刚刚我们通过减而治之入手,最后通过分而治之找到了解题的突破口,现在我们需要回到减而治之,再从更宏观的角度去探索可供优化的空间了,那就是dp 状态转移方程!
- 我们最终要解决的是一个的切分问题,而切分的点都在上,假设中不存在呢?那显然,这就是我们的第一个方程。接着,假设有一个,例如串为,基于以上那么多那么多的分析,我们知道,但如果我们仔细看一下,这个式子还可以写成,也即,这难道不就是我们再熟悉不过的转移方程吗?也就是说,每个只与上一个时的状态和上一个元素时的状态有关!毫无疑问,我们可以通过一个一维数组记录每个点的状态,然后一趟完成遍历即可!这就是本题第二个最关键的点:每个只与和的状态有关,即因此只需要通过一个一维数组 dp 记录每个点的状态即可。
- 我们现在来到了最后一个问题,如何从得到上一个呢?很显然,暴力搜索是可以实现的,只不过效率太低。或者,我们在程序的开始,就直接算出个元素之间的彼此是否互质关系,只不过效率依旧不高。其实我们不妨再思考一下,由于只和上一个和有关,因此我们只需要对于每个倒序搜索到第 个就可以,不用把所有的元素都给乘一遍。尽管如此,我们的效率依旧不是很高,因为每个也要去乘,这样,这就是一个链,几乎还是每个元素都乘一遍,尤其是那些可能与所有元素都不互质的元素来说。我们不禁重新思考这个的意义。
- 【看题解,要结合质数筛】