目录
剪绳子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
方法一:动态规划
乍一看去题目没有多大变化,我直接套用上一题的代码,最后结果取余,没想到直接答案错误。
原来本题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开始切割会得到最优解,此时我们就不能直接返回,因为会超出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);
}
}
方法三:快速幂
其实主体思路和上面的贪心差不多,只不过我们自定义了求幂的函数,让函数可以快速得到循环取余的结果,
我们在正常求解的解法是循环乘n次x,时间复杂度为,而快速幂的方法将时间复杂度降到了,
假设我们此时要求解,我们先将11转换为二进制数1011,则原式子可以转换为:
此时只计算了三次乘积,那么我们如何计算呢,思路大致为:
- 对二进制数的末位进行判断,为1的话我们才将结果乘上该位的权重,为0直接跳过
- 权重累乘,更新当前位的权重
- 二进制数右移一位,直至二进制数为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;
}
}