整数拆分——动态规划基础题

343 整数拆分

给定一个正整数 n ,将其拆分为 k 个 正整数 的和( k >= 2 ),并使这些整数的乘积最大化。返回 你可以获得的最大乘积 。

 思路

重做本题,实属是卡壳了一点(还是太菜了)。看到题目要求后有些无从下手,题目要求将给定的一个正整数,拆分成 2 个及以上的正整数的和,并且这些正整数的和的乘积最大,求该最大乘积。大概思考了一下,拆分?还能拆成多个正整数?并且从例子看得出,可以由重复的正整数凑成(如全部都是1),前面几篇博客中所求状态都和之前的两个状态有关,这个题……关联的也好像有点多,因为是要把一个整数拆分,那么我可以创建个一维数组,并且索引值1~n代表每个连续的数,如:

为什么连续?因为一个数可以由多个整数以不同的数量相加得到,如 数字 5 可以是 5个1之和、1与4之和、2与3之和。那下面就得思考:怎么做?某一个数字的 dp[i] 该如何得到,这一定和该数字前面的数字的 dp 是相关的(这样递推公式存在才有意义:即根据前面的状态利用递推公式推出后面的状态),那,这某个数字的 dp[i] 有哪些来源?

我们看看下面的推导,我们使用两个下标 i 和 j ,其中 i 代表待拆分的整数,j 代表从 1 开始遍历的指针。当要拆分的整数 i = 2 时,可以得到最大乘积为 1,当要拆分的整数 i = 3 时,可以得到最大乘积为 2,以此类推......

 

从上图的推导以及抽象可以得到,dp[i] 的来源有两个:(i - j)*j (i - j 和 j 本身所代表的数之积)、dp[i - j] * j (j 所代表的正整数同 i - j 所代表的正整数对应的 dp[i - j] 之积)。记住 dp[ i ] 的含义为将整数 i 拆分使得这些整数的乘积最大的此最大值。按照题目要求,我们需要找到最大值,以此取这两者的最大值即可,初步确定递推公式为:

dp[i] = Math.max(i * (i - j), i * dp[i - j]);

下面使用动态规划五部曲解。

确定dp数组含义

正整数 i 可以拆分成两个或两个以上的正整数之和,求得的这多个正整数之积的最大值为 dp[i]。

确定递推公式

在上面得出的递推公式的基础上,还需要和dp[ i ]比较得到最大值。即最终递推公式如下:

dp[i] = Math.max(dp[i], Math.max(dp[i - j) * j, (i - j) * j);

 怎么取完最大值还要和 dp[i] 再去比较最大值?这个可以下面看完遍历顺序以及整体代码后来理解,就懂了。这样子做是因为对于一个正整数 i ,我们是用 正整数 j 来遍历从而得到 dp[i] ,而 j 是从 1 遍历到 i - 1 ,意味着在循环 j 的过程中,每循环一次就要执行一次递推公式,等到 j 都遍历完了也就找到了满足条件的 dp[i] ,这个过程一定是取最大的那个 dp[i],故还需和 dp[i] 比较取最大值。

初始化 dp 数组

按照我们上面的数组示意图来分析,下标代表一个数,0 则不用初始化,不可能找到两个或两个以上的正整数之和为 0 ;那么 1 呢?也是不可能找到两个及以上的正整数之和为 0 ,为什么?小学初中知识:0 不是正整数!,因此初始化 0 和 1 是没意义的。这里是Car哥的理解,是非常到位的,再一次做这一题时笔者是初始化 i = 1,也可以通过测试用例,但通过归通过,实际上在含义上是不正确的,这里需要明确。

dp[1] = 1;

确定遍历顺序

从上面的思路分析中,可以得出,i 是从左往右遍历的,j 也是从左往右遍历的,且外层for循环遍历 i 内层循环遍历 j ,因为我们是每次固定一个正整数 i ,找出该正整数的满足要求的 dp[i] ,i 一定是来源于比自己小的数求和得到的(本题的数都是正整数)所以 j 从 1 遍历到 i - 1。

for(int i = 2; i <= n; i++) {
    for(int j = 1; j < i; j++) {
        dp[i] = Math.max(dp[i], Math.max(j * (i - j), j * dp[i - j]));
    }
}

举例推导递推公式

代码实现

class Solution {
    public int integerBreak(int n) {
        int[] dp = new int[n + 1];
        dp[1] = 1;
        for(int i = 2; i <= n; i++) {
            for(int j = 1; j < i; j++) {
                dp[i] = Math.max(dp[i], Math.max(j * (i - j), j * dp[i - j]));
            }
        }
        return dp[n];
    }
}

《代码随想录》刷题记

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值