剑指 Offer 14- I. 剪绳子(动态规划+数学推导)剑指 Offer 14- II. 剪绳子 II(大数运算+数学推导)

剑指 Offer 14- I. 剪绳子

问题描述

给你一根长度为 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。

示例 1:

输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1

示例 2:

输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36

提示:

    2 <= n <= 58

解题思路

解法一:(动态规划)

本题求解剪绳子的长度最大乘积。即最值问题。

当剪第一次时,有n-1个选择。因此dp[n]=dp[1]dp[n-1].

即目标问题可以划分为具有相同子结构的子问题,因此使用动态规划求解。

使用动态规划解决问题的五步:

  • 确定dp数组(dp table)以及下标的含义

dp[i],这里代表的是长度为i的绳子剪完的最大乘积。

  • 确定递推公式

根据上述分析,递推公式为:dp[n]=dp[i]dp[n-i].

  • dp数组如何初始化

由于n>1且m>1,因此n从2开始。

n=2时(m>1的情况下),n=1+1 ,dp[n] = 1;

n=3时(m>1的情况下),n=1+2,dp[n] = 2;

需要注意的是,上述初始值只适用于n<=3的情况,当n>=4时。

dp[2]的值实际上为2,dp[3]的值实际上为3.(切割之后值变小)。

因此遍历中,n>=4时,初始值为:dp[2]=2;  dp[3] = 3.

  • 确定遍历顺序

由于此问题具有重复子问题,因此需要记录下所有的子问题最优解。即需要自下往上计算。

  • 举例推导dp数组

解法二:数学推导

使用数学推导得到:① 当所有绳段长度相等时,乘积最大。② 最优的绳段长度为 3 。

因此切分规则为:

最优: 3 。把绳子尽可能切为多个长度为 3 的片段,留下的最后一段绳子的长度可能为 0,1,2三种情况。
次优: 2。若最后一段绳子长度为 2;则保留,不再拆为 1+1 。
最差: 1 。若最后一段绳子长度为 1 ;则应把一份 3+1 替换为 2+2,因为 2×2>3。

作者:jyd(Leecode)

Java解法

解法一:

class Solution {
    public int cuttingRope(int n) {
        int [] dp = new int [n+1];
        if(n<2) return 0;
        if(n==2) return 1;
        if(n==3) return 2;
        dp[2] = 2;
        dp[3] = 3;
        for(int i=4;i<=n;i++){
            int max = 0;
            for(int j=2;j<=i/2;j++){  //j<i也可以,使用i/2的原因是 1*3和3*1的值实际上是一样的
                max = Math.max(max,dp[j]*dp[i-j]);
            }
            dp[i] = max;
        }
        return dp[n];

    }
}

解法二:

class Solution {
    public int cuttingRope(int n) {
        if(n<2) return 0;
        if(n==2) return 1;
        if(n==3) return 2;
        int a = n/3;
        int b = n%3;
        if(b==0) return (int)Math.pow(3,a);
        else if(b==1) return (int)Math.pow(3,a-1)*4;
        return (int)Math.pow(3,a)*2;

    }
}

剑指 Offer 14- II. 剪绳子 II

问题描述

给你一根长度为 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。

示例 1:

输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1

示例 2:

输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36

提示:

    2 <= n <= 1000

解题思路

解法一:(动态规划)

动态规划思路与上题类似。但由于2 <= n <= 1000,因此如果使用Math.max来判断对1000000007取模后的值的大小,就会出现错误。

因此使用大数BigIntger替换int类型。进行运算。

但是使用BigInteger类型会特别慢,耗时也很久。

解法二:循环取余解法

按照题目要求,计算结果可能 会 超出int范围
因此,我们要做到的是:每一步 乘法运算 都要有 取余操作
只有这样,才能保证 运算结果正确性

Java解法

解法一:

import java.math.BigInteger;
class Solution {
    public int cuttingRope(int n) {
        BigInteger dp [] = new BigInteger [n+1];
        Arrays.fill(dp, BigInteger.valueOf(1));
        if(n<2) return 0;
        if(n==2) return 1;
        if(n==3) return 2;
         for (int i = 3; i <= n; i++) {
            for (int j = 1; j < i; 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();
    }
}

解法二:

class Solution {
    public int cuttingRope(int n) {
        if(n<2) return 0;
        if(n==2) return 1;
        if(n==3) return 2;
        int threadhold = 1000000007;
        long result = 1L;
        while (n > 4) {
            result = result * 3 % threadhold;
            n -= 3;
        }
        result = result * n % threadhold;
        return (int)result;
    }
}

猜你喜欢

转载自blog.csdn.net/paranior/article/details/115401969