题目来自LeetCode,链接:面试题14- I. 剪绳子。具体描述:给你一根长度为 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。
示例1:
输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1
示例2:
输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36
首先讲一下我自己的做法,根据均值不等式我们知道要使乘积最大,那么就要使各段的长度最接近,比如分成3,3
就比4,2
好,所以可以这么做,先均分成m段整数长度,如果还有剩下的长度再继续均分(这个时候只有部分能分到了)。举个例子,n=11
的情况下,假设m=3
,先均分成3段长度为3的,然后还剩下长度11-9=2
,再将这个个长度2
均分给其中两段得到4,4,3
,这就是在3段的情况下的最好情况了,然后我们需要遍历m
从2~n
的所有情况,返回其中的最大结果。因为pow()
的时间复杂度是 O ( 1 ) O(1) O(1),所以总时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( 1 ) O(1) O(1)。
JAVA版代码如下:
class Solution {
public int cuttingRope(int n) {
int result = 0;
for (int i = 2; i <= n; ++i) {
int ave = n / i;
int res = n % i;
int cur = (int)(Math.pow(ave, i - res) * Math.pow(ave + 1, res));
result = Math.max(result, cur);
}
return result;
}
}
提交结果如下:
Python版代码如下:
class Solution:
def cuttingRope(self, n: int) -> int:
result = 0
for i in range(2, n + 1):
ave = n // i
res = n % i
cur = (int)(math.pow(ave, i - res) * math.pow(ave + 1, res))
result = max(result, cur)
return result
提交结果如下:
然后是动态规划的方法,假设dp[i]
代表长度为i
的绳子所能得到的最大乘积。假设这里最后一段剪出来的绳子长度为i-j
(j
的范围为1~i-1
),那么前面长度为j
的绳子的最大乘积即为dp[j]
(也被剪成段了)或j
(没被剪成段),所以dp[i]=max(j*(i-j), dp[j]*(i-j))
,这就是我们需要的递推公式。因为需要两层循环,所以时间复杂度为 O ( n 2 ) O(n^{2}) O(n2),空间复杂度为 O ( n ) O(n) O(n)。
JAVA版代码如下:
class Solution {
public int cuttingRope(int n) {
int[] dp = new int[n + 1];
for (int i = 2; i <= n; ++i) {
for (int j = 1; j < i; ++j) {
dp[i] = Math.max(dp[i], Math.max(j * (i - j), dp[j] * (i - j)));
}
}
return dp[n];
}
}
提交结果如下:
最后还有一种需要数学推导的方法,具体看这里。时间复杂度和空间复杂度均为 O ( 1 ) O(1) O(1)。
JAVA版代码如下:
class Solution {
public int cuttingRope(int n) {
if (n <= 3) {
return n - 1;
}
int p = n / 3;
int q = n % 3;
if (q == 2) {
return (int)Math.pow(3, p) * 2;
}
if (q == 1) {
return (int)Math.pow(3, p - 1) * 4;
}
return (int)Math.pow(3, p);
}
}
提交结果如下: