给你一根长度为 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
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jian-sheng-zi-lcof
数学推导:
给出一个长度n,并且x1 + x2 + x3 +…+xn= n,那么这时候需要求解x1x2x3*…xn的最大值即可。那么怎么求解呢?
我们高中的时候学过不等式(x + y) / 2 >= √(xy),所以xy的最大值就是在x = y的时候,xy有最大值。所以我们这里就可以将这个性质应用到这里。所以在已知这些数的和已知的情况下,将绳子剪成等长可以使得最后结果的乘积最大。所以关键怎么知道要将绳子剪多长?
请看下面的证明过程:
在绳子总长大于3的时候,需要将绳子剪成div分等长的并且长度为3的绳子。已经知道了将绳子剪成等长3的时候,我们需要分3中情况讨论:
①当绳子刚好整除3,那么最后的结果就是3 ^ div
②当绳子不能整数3的时候,并且余数为1,那么这时候我们需要将一份长度为3的绳子保留,这时候就剩下div - 1分长度为3的等长绳子和1分长度为4的绳子。这时候将这个长度为4的绳子剪成2 + 2的绳子,才会有最大值。此时最大值为3 ^(div - 1) * 4.
③当余数为2的时候,不需要将2这个绳子再进行分割,而是保留。此时最大值为3 ^ div * 2.
对应的代码:
class Solution {
public int cuttingRope(int n) {
if(n == 2)
return 1;
if(n == 3)
return 2;
int div = n / 3;//将n分成等3长度
int result = (int)Math.pow(3,div);
//System.out.println(div + ", " + result);
if(n % 3 == 1){
return (result / 3 ) * 4;//如果不能整除3,那么就将最后一份3加1化成2 * 2
}else if(n % 3 == 2){
//如果最后还剩下2,那么就将这个2直接保留,而不是将2化成1+1
return result * 2;
}else{
return result;//整除3的时候
}
}
}
运行结果:
给你一根长度为 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
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jian-sheng-zi-ii-lcof
基于上面的题目,我们来解决这个问题。但是如果是只是上面的代码的结果result % 1000000007的话,那么就会发生报错,为什么?因为如果测试的例子是100的时候,最后返回的是一个负数,说明发生了越界。所以为了避免这个问题,我们需要不断取余。因此这个题目还考察了大数取余。所以在取余的时候,还需要了解这个公式:
(x \* y) % p = [(x % p) \* (y % p)] %p
,刚开始我对这个公式感到疑惑的是,为什么最后还需要[x %p][y %p]这个数在模p呢?后来通过距离例子可以证明:如果p = 6,x * y = 6,假设x = 2, y = 3,那么如果最后没有在模6的话,那么[2 % 6][3 % 6] = 6,而不是我们想要的0,因此最后还要再模p。
对应的代码:
class Solution {
public int cuttingRope(int n) {
if(n <= 3)
return n - 1;
long div = n / 3,num;//这个绳子能分成3的多少分
long res = 1;
num = div - 1; //为了避免存在最后剩余长度为1的情况,所以先计算div - 1等长的积对1000000007的模
//循环取模法
while(num > 0){
res = (res * 3) % 1000000007;
num--;
}
if(n % 3 == 1){
//如果最后余1,那么需要取3的div - 1次方,最后4分成2 * 2
res = (res * 4) % 1000000007;
}else if(n % 3 == 2){
/*
注意这里不可以是(res * 3 * 2) % 1000000007,因为有可能res * 3之后
大于了1000000007
*/
res = (((res * 3) % 1000000007) * 2) % 1000000007;
}else{
//刚好整除的时候,那么需要乘以3,将最后一份长度为3的绳子相乘
res = (res * 3) % 1000000007;
}
return (int)res;
}
}
运行结果: