剑指 offer|14 剪绳子

2022-11-21 20:26:47 浏览数 (3)

题目描述

代码语言: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();
  }
}

1 人点赞