剑指 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。
解答
class Solution {
/**
剪绳子乘积的最大值的推论
推论一:将绳子以相等的长度等分为多段,得到的乘积最大
推论二:尽可能将绳子以长度3等分为多段时,乘积最大
由推论可得出,切分规则为:
最优:3。把绳子尽可能切为多个长度为3的片段,留下的最后一段绳子的长度可能为0,1,2三种情况,
留下的长度为0为最优,代表全部等分为长度为3的片段
次优:2。若最后一段绳子长度为2;则保留,不再拆分为1+1
最差:1。若最后一段绳子长度为1;则应把一份3+1替换为2+2,因为2 x 2 > 3 x 1
算法流程:
当n <= 3时,按照规则应不切分,但由于题目要求必须剪成m>1段,因此必须剪出一段长度为1的绳子,即返回n-1
当n > 3时,求n除以3的整数部分a和余数部分b(即n = 3a+b),并分为以下三种情况
当b=0时,直接返回3^a % 1000000007
当b=1时,要将一个1+3转换为2+2,因此返回 (3^a-1 × 4)%1000000007
当b=2时,返回(3^a × 2)%1000000007
当 a 增大时,最后返回的 3^a大小以指数级别增长,可能超出 int32 甚至 int64 的取值范围,导致返回值错误。
所以我们可以在计算的过程中对中间的计算结果取余以确保最后计算结果的准确性。
求余运算规则 (xy)%p = [(x%p)(y%p)]%p
由求余运算规则,可得x^a%p = ((x^a-1 % p)x)%p //循环求余法,即在a个x乘在一起时,每乘一个x对p取一次余
*/
public int cuttingRope(int n) {
if(n <= 3){
return n-1;
}
int a = n/3;
int b = n%3;
// res*3可能在求余之前超出int的取值范围,所以用long
long res = 1;
int p = 1000000007;
for(int i = 1;i < a;i++){ //因为最后一个3可能根据余数的不同可能需要拆分,所以在这先成a-1个3
res = (res * 3)%p;
}
if(b==0){ //最优
res = (res * 3)%p;
}else if(b == 2){ //次优
res = (res * 3 * 2)%p;
}else if(b == 1){ //最差
res = (res * 2 * 2)%p;
}
return (int)res;
}
}