剑指Offer_#14-2_剪绳子
Contents
题目
给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m]
。请问k[0]*k[1]*...*k[m]
可能的最大乘积是多少?例如,当绳子的长度是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
思路分析
和前一题相比,唯一的区别就是这里的输入范围变大的,所以导致最后的结果可能会超过int所表示的最大范围,所以需要考虑如何求这个大数的余数。
我觉得这个题更像是一道数学题,而不是什么动态规划...
题解1
这是二分求余数法,其实我不完全理解,为什么遇到奇数需要特殊处理,之后再多看几遍吧。
class Solution {
public int cuttingRope(int n) {
if(n <= 3) return n-1;
int b = n % 3,p = 1000000007;
long rem = 1,x = 3;
for(int a = n/3 - 1;a > 0;a /= 2){//a的初始值为什么是n/3-1?这是长度为3的绳子段数。循环过程就是计算3^a%p
if(a % 2 == 1) rem = (rem * x) % p;//逢奇数,需要特殊处理
x = (x * x) % p;
}
if(b == 0) return (int)(rem * 3 % p);
if(b == 1) return (int)(rem * 4 % p);
return (int)(rem * 6 % p);//不理解为什么乘以6?
}
}
复杂度分析
由于每次指数减小一倍,时间复杂度是
题解2
贪心算法,也就是直接分析出每一次都是剪下长度为3的绳子最好,所以直接通过循环模拟这个过程,每次绳子长度减小3,结果乘以3。
循环结束的结果分为三种:
1.n=2,等于说无限除以3,最后余下绳子长度为2,此时将res乘以2即可
2.n=3,绳子全部用完,直接所有3相乘即可
3.n=4,等于说余下绳子长度为1,因为4%3=1,但是3<2*2,也就是4本身,故最后乘4
这里的求余方式是循环求余,复杂度稍高,但是更加直观。
循环求余法的依据是
也就是说,每一轮都进行一次求余操作,再迭代,和先迭代完成后再进行求余操作,结果相同。
class Solution {
public int cuttingRope(int n) {
if(n<=3) return n-1;
long res=1;
while(n>4){
res*=3;
res=res%1000000007;
n-=3;
}
return (int)(res*n%1000000007);
}
}
复杂度分析
相比二分求余法,这个方法的复杂度稍高,是