2021-11-30 343. 整数拆分(动态规划)

注:

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

示例 1:
输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1。
示例 2:
输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。
说明: 你可以假设 n 不小于 2 且不大于 58。

题解:
动态规划
动规五部曲,分析如下:

确定dp数组(dp table)以及下标的含义
dp[i]:分拆数字i,可以得到的最大乘积为dp[i]。

确定递推公式
可以想 dp[i]最大乘积是怎么得到的呢?

其实可以从1遍历j,然后有两种渠道得到dp[i]:
一个是j * (i - j) 直接相乘。
一个是j * dp[i - j],相当于是拆分(i - j),对这个拆分不理解的话,可以回想dp数组的定义。

除此之外,在递推公式推导的过程中,也需要比较上次计算得到的dp[i]值和上述两种渠道得到的dp[i]的值,取三者之间的最大值。

递推公式:dp[i] = max(dp[i],max((i-j)*j,dp[i-j]*j))

也可以这么理解,j * (i - j) 是单纯的把整数拆分为两个数相乘,而 j * dp[i - j]是拆分成两个以及两个以上的个数相乘。

如果定义dp[i - j] * dp[j] 也是默认将一个数强制拆成4份以及4份以上了。

dp的初始化
严格从dp[i]的定义来说,dp[0] dp[1] 就不应该初始化,也就是没有意义的数值。

拆分0和拆分1的最大乘积是多少?这是无解的。

这里只初始化dp[2] = 1,从dp[i]的定义来说,拆分数字2,得到的最大乘积是1,这个没有任何异议!

确定遍历顺序
确定遍历顺序,先来看看递归公式:dp[i] = max(dp[i],max((i-j)*j,dp[i-j]*j))

dp[i] 是依靠 dp[i - j]的状态,所以遍历i一定是从前向后遍历,先有dp[i - j]再有dp[i]。

枚举j的时候,是从1开始的。i是从3开始,这样dp[i - j]就是dp[2]正好可以通过我们初始化的数值求出来。

举例推导dp数组
举例当n为10 的时候,dp数组里的数值,如下:
2021-11-30 343. 整数拆分(动态规划)_第1张图片

复杂度分析
时间复杂度:O(n2),其中 n 是给定的正整数。对于从 2 到 n 的每一个整数都要计算对应的 dp 值,计算一个整数对应的 dp 值需要 O(n) 的时间复杂度,因此总时间复杂度是 O(n2)。

空间复杂度:O(n),其中 n 是给定的正整数。创建一个数组 dp,其长度为 n+1。

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

你可能感兴趣的:(贪心算法,算法,动态规划)