题目描述
给你一根长度为 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
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题思路
思路一:遍历最优“段数”寻找最优解
段数最少为2,最大为n/2+1(或者说n/2向上取整),在这个区间内进行遍历,如此一来,假设当前段数为m,那么每段的最小长度为n/m,此时会余下n%m的长度,将余下的长度给n%m个段多分配1个单位,然后计算乘积即可。最后选出最大乘积。
思路二:如果把数字范围扩展到全体正数,那么利用均值不等式获得的最优解其实是:
f ( m ) = ( n / m ) m f(m)=(n/m)^m f(m)=(n/m)m
其中m为段数。对m通过对数求导可以算出当 m = n / e m=n/e m=n/e 时取得最小值,此时每段的长度为 e = 2.71828... e=2.71828... e=2.71828...,自然数3是最接近的,因此尽可能地把绳子切成多个长度为3的片段,此时余数会有0、1、2三种情况
- 若余数为0,说明切分得很正好。但此时会产生一种情况:就是n=3。由于段数必须大于等于2,所以必须拆分成1和2。
- 若余数为1:则应该把一份3+1替换成2+2,因为2×2>3×1
- 若余数为2:保留,不继续拆分成1+1。但此时会产生一种情况:就是n=2。由于段数必须大于等于2,所以必须拆分成1和1。
代码详解
思路一:遍历寻优
class Solution {
public int cuttingRope(int n) {
int ans = 1;
for(int i = 2; i <= n/2+1; ++i) { // i为段数,遍历的最小值为2,最大值为n/2向上取整
int temp = 1; // 累乘
int len = n / i; // 每段最小长度
int rest = n - i * len; // 余下的长度,需要分给某些段各1个单位长度
for(int j = 0; j < i; ++j) {
if(j < rest) {
temp *= (len+1); // rest个段数需要乘上(len+1)
} else {
temp *= len; // 其余段数只需乘上len
}
}
ans = Math.max(ans, temp); // 取最大值
}
return ans;
}
}
思路二:数学法
class Solution {
public int cuttingRope(int n) {
if(n <= 3) { // 特殊情况
return n-1;
} else {
return (int)Math.pow(3, n/3) * 4 / (4 - n%3);
// (int)Math.pow(3, n/3)这块应该都懂
// * 4 / (4 - n%3)这部分分为三种情况
// 若n%3=0,则*4/4=1,表示不再多乘
// 若n%3=1,则*4/3,表示把一个(3,1)(也就是除以3)替换成(2,2)(也就是乘以4)
// 若n%3=2,则*4/2=2,保留余数2
}
}
}
注意点
- 遍历法没超时我是没想到的(做完剪绳子II之后发现,因为题目限定n很小而已,再大越long上界需要取余的时候遍历法就不能用了),这题的数学法还要用到求导我又是没想到的
- 友情链接:【LeetCode】剑指14—— II. 剪绳子II(难度:中等)