前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >剑指 offer|14 剪绳子

剑指 offer|14 剪绳子

作者头像
孟君
发布2022-11-21 20:26:47
2810
发布2022-11-21 20:26:47
举报
文章被收录于专栏:孟君的编程札记

题目描述

代码语言:javascript
复制
给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,
n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m-1] 。
请问 k[0]*k[1]*...*k[m-1] 可能的最大乘积是多少?
例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,
此时得到的最大乘积是18。

来源:https://leetcode.cn/problems/jian-sheng-zi-lcof

示例1:

代码语言:javascript
复制
输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1

示例2:

代码语言:javascript
复制
输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36

提示:

代码语言:javascript
复制
2 <= n <= 58

解题分析

我们从题目中看到,长度N的绳子切割后,分段值乘积最大的结果其实与较短的结果相关,比如分成2段,可以是1和6,2和5以及3和4,所以如果考虑乘积,我们可以考虑动态规划的思想,把每段的最大值存储下来。

代码语言:javascript
复制
int[] dp = new int[n + 1];

//dp[i]表示长度为i的切割段的最大乘积,其中:
dp[0]  = 0; //不能分段
dp[1]  = 0; //不能分段,记为0
dp[2]  = 1; //只能分成2段1和1, 乘积为1 * 1 = 1

通过2层循环解决,

1、外层循环:因为dp[0]、dp[1]和dp[2]的值都已知,不是0就是1,所以,求乘积,我们外层从dp[3]开始。

2、内存循环:比对当前分成2段 (i -j) * j的值,和其他分段的最大乘积的值dp[i-j][j],两者之间的值,取大值。

代码语言:javascript
复制
 dp[i] = Math.max(dp[i],Math.max(j*(i-j),j*dp[i-j]));

有了这个思路,我们来写出一个dp解法:

方法1

代码语言:javascript
复制
class Solution {
  public int cuttingRope(int n) {

    int[] dp = new int[n + 1];

    dp[0] = 0; // 不能切断
    dp[1] = 0; // 不能切断
    dp[2] = 1; // 只能是1 * 1 = 1

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

由于分段有一定的对称性,比如N = 7, 分成2段,1和6,2和5,3和4, 4和3,5和2,6和1,所以可以看出,有一定的对称性。所以,内循环的范围可以只到中间值:

代码语言:javascript
复制
for(int j = 2; j <=(i+j) /2; j++) {
... ...
}

调整后的代码:

代码语言:javascript
复制
class Solution {
  public int cuttingRope(int n) {

    int[] dp = new int[n + 1];

    dp[0] = 0; // 不能切断
    dp[1] = 0; // 不能切断
    dp[2] = 1; // 只能是1 * 1 = 1

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

做完了14-I,我们发现还有14-II。

题目描述:剑指offer 14-II. 剪绳子II

代码语言:javascript
复制
给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段
(m、n都是整数,n>1并且m>1),
每段绳子的长度记为 k[0],k[1]...k[m - 1] 。请问 k[0]*k[1]*...*k[m - 1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。

答案需要取模 1e9+7(1000000007),
如计算初始结果为:1000000008,请返回 1。

来源:https://leetcode.cn/problems/jian-sheng-zi-ii-lcof

示例1:

代码语言:javascript
复制
输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1

示例2:

代码语言:javascript
复制
输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36

提示:

代码语言:javascript
复制
2 <= n <= 1000

这个14-II与14-I的区别是对于较大的数字需要取模, 如:

代码语言:javascript
复制
答案需要取模 1e9+7(1000000007),
如计算初始结果为:1000000008,请返回 1

大数计算,我们可以使用java.math.BigInteger来替代int,如:

代码语言:javascript
复制
import java.math.BigInteger;
import java.util.Arrays;

class Solution {
  public int cuttingRope(int n) {

    BigInteger[] dp = new BigInteger[n + 1];
    Arrays.fill(dp, BigInteger.valueOf(0));
    dp[0] = BigInteger.valueOf(0); // 不能切断
    dp[1] = BigInteger.valueOf(0); // 不能切断
    dp[2] = BigInteger.valueOf(1); // 只能是1 * 1 = 1

    for (int i = 3; i <= n; i++) {
      for (int j = 2; j <= (i + j) / 2; j++) {
        dp[i] = dp[i].max(BigInteger.valueOf(j * (i - j))).max(dp[i - j].multiply(BigInteger.valueOf(j)));
      }
    }
    return dp[n].mod(BigInteger.valueOf(1000000007)).intValue();
  }
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-06-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 孟君的编程札记 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 题目描述
  • 解题分析
  • 方法1
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档