vlambda博客
学习文章列表

动态规划2 -------整数拆分

突然想起来,关于算法方面的描述大部分人是不看的,没有必要搞模板把排版搞得花里胡哨的,直接写就是完事了。


题目描述:

给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。返回你可以获得的最大乘积。


示例 1:


输入: 2

输出: 1

解释: 2 = 1 + 1, 1 × 1 = 1。

示例 2:


输入: 10

输出: 36

解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。

说明: 你可以假设 n 不小于 2 且不大于 58。



题注:本题难点在于不知道会将一个数拆成几个数相加,如果只是两个数的话很好做,但是示例2已经说明不是两个数了,个人觉得这种题还是得往动态规划上去靠。目前为止,这就是我脑子里关于这道题理解的所有知识储备了, 看了题注之后就发现原来这道题是这么简单,这里做一下引用,结尾贴出来引用文章。


看完题解之后的分析:

鬼都知道这道题应该需要递归,那么如何层次把题目剥离出来,是非常关键的,

这道题抽象一下就是:

令:(图 1)

求:

(图 2)

当然这里ni是未知的,多少个数是不知道的,每个数的大小也是不知道的。昨天和胖子聊我对动态规划的感悟是,别管中间的过程变量,只要了解,过程中应该能得到的最优的值就行了,用动态规划的方法层层剥离才是我们需要掌握的正确方式。这样,我们剥第一层洋葱,我们可以把给定的n想象为两个数相乘,比如原来的n可以由5个数相加,并且这5个数相乘是可以得到最大的结果的,那么我们想让前四个数相乘,然后用最后一个数和前面的数相乘就行了吧,原理上是一样的,

  • 我们将原问题抽象为 f(n)
  • 那么 f(n) 等价于 max(1 * fn(n - 1), 2 * f(n - 2), ..., (n - 1) * f(1))。

核心在于这个max,同时不能忘了,有时候fn(n-1) <n-1,所以max需要比较一个list就是 max({MAX, j*(i-j),  j*fn(i-j)})

用数学公式表示就是:

(图 3)

子问题依旧可以转化为一个单独的问题去求解,这种嵌套式的递归结构时间复杂度为O(n^n),但是明显是可以使用引入辅助数组来让时间复杂度猛降的,这种数组在动态规划中经常用到,只不过我在学习的过程中总是习惯记他的形式,而忘记了数组设计本身存在的意义,数组是将递归转化为非递归的一种重要手段,因为递归的函数记忆性不高,使用数组来提高记忆力,最终就可以转化为求一个长度为n的数组,里面每一个序列对应的值都是序列值所能取得最大乘积的结果。仔细一看这个不是浩哥说的小青蛙变态爬楼梯版吗。爱了爱了。


递归方法引用python的了,下面的c++方法我自己撸。class Solution: def integerBreak(self, n: int) -> int: if n == 2: return 1 res = 0 for i in range(1, n): res = max(res, max(i * self.integerBreak(n - i),i * (n - i))) return res


关于动态规划的问题,我想引用原作者的一句话,动态规划就是建表查表,


贴一下c++代码:

class Solution {public: int integerBreak(int n) { vector<int> dp(n+1, 0); int MAX; dp[1] = 1; for (int i = 2; i <= n ; ++i) { MAX = 0; for (int j = 1; j < i; ++j) MAX = max({MAX, j*dp[i-j], j*(i-j)}); dp[i] = MAX; } return dp[n]; }};