剑指Offer(14-2)剪绳子II

目录

剪绳子II

描述

示例 1

示例 2

提示

方法一:动态规划

方法二:贪心

方法三:快速幂


剪绳子II

描述

给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m - 1] 。请问 k[0]*k[1]*...*k[m - 1] 可能的最大乘积是多少?

扫描二维码关注公众号,回复: 13161561 查看本文章

例如,当绳子的长度是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

方法一:动态规划

乍一看去题目没有多大变化,我直接套用上一题的代码,最后结果取余,没想到直接答案错误。

原来本题n的范围变大了,导致int类型范围无法存储,后来我将中间的值也取余试图将值的范围控制下来,发现还是有问题。

比如我们在根据递推关系求解dp[i]时,加上了中间结果的取余,得到下列式子:

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

假设此时刚好dp[i]=1000000006,而后面的max求解出来的值为1000000008,进行取余操作之后变成了1,所以此时dp[i]从1000000006和1之间取最大值变成了1000000006,而不是正确的1000000008%1000000007=1,所以就会导致后面的结果也接连出现问题。

要想解决这个问题,需要用到java中内置的大数类BigInteger,

import java.math.BigInteger;

class Solution {
    public int cuttingRope(int n) {
        BigInteger[] dp=new BigInteger[n+1];//创建动态规划的大数数组
        Arrays.fill(dp,BigInteger.valueOf(1));//对大数数组进行赋值
        for (int i = 3; i < n+1; i++) {
            for (int j = 2; 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();//对结果进行取余
    }
}

 该方法比较慢,因为用到了BigInteger类,计算非常耗时。

方法二:贪心

同样我们可以得到将绳子尽量按长度3开始切割会得到最优解,此时我们就不能直接返回3^a,因为会超出int的范围,我们使用循环不停累乘即可,

class Solution {
    public int cuttingRope(int n) {
        if(n < 4){
            return n - 1;
        }else if(n == 4){
            return n;
        }
        long res = 1;
        while(n > 4){
            res *= 3;
            res %= 1000000007;
            n -= 3;
        }
        return (int) (res * n % 1000000007);
    }
}

方法三:快速幂

其实主体思路和上面的贪心差不多,只不过我们自定义了求幂的函数,让函数可以快速得到循环取余的结果,

我们在正常求解x^n的解法是循环乘n次x,时间复杂度为O(n),而快速幂的方法将时间复杂度降到了O(log_2 n)

假设我们此时要求解x^{11},我们先将11转换为二进制数1011,则原式子可以转换为:

x^{11}=x^{1*2^3+0*2^2+1*2^1+1*2^0}=x^{2^3}\times x^{2^1}\times x^{2^0}

此时只计算了三次乘积,那么我们如何计算呢,思路大致为:

  1. 对二进制数的末位进行判断,为1的话我们才将结果乘上该位的权重,为0直接跳过
  2. 权重累乘,更新当前位的权重
  3. 二进制数右移一位,直至二进制数为0

二进数的运算我们需要用到位运算符,

"&"的作用是按位取与,如果我们将n&1,那么就可以知道n的末位是否为0。

">>"的作用是二进制数右移,如果n>>1,就可以将n的二进制数右移1位。

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

    public long myPow(long base, int exp){
        long res = 1;
        while(exp > 0){
            if((exp & 1) == 1){
                res *= base;
                res %= 1000000007;
            }
            base *= base;
            base %= 1000000007;
            exp >>= 1;
        }
        return res;
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_39478524/article/details/120539657