1. 题目来源
链接:剪绳子I、剪绳子 II
来源:LeetCode——《剑指-Offer》专项
2. 题目说明
给你一根长度为 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
提示:
2 <= n <= 58
3. 题目解析
方法一:动规解法
针对 剪绳子I:
简单的动规思路,定义 f(n)
为把长度为 n
的绳子所得到的最大值。当剪第一刀时有 n - 1
种选择。那么就能得到状态转移方程 f(n) = max(f(i) * f(n - 1))
,其中 0 < i < n
。
然而这是一个自上而下的递归公式,会产生大量的重复计算,将其转为自下而上的方法即可解决该问题,也就是说 首先得到 f(2)、f(3)
,再得到 f(4)、f(5)
,最后得到 f(n)
。
定义几个初始状态,即 f(2) = 1
由于必须剪一刀,那么就从中间剪开,得到 1 x 1 = 1
,同理能得到 f(3) = 2
。
创建 vector
数组 vt
用以存放子问题的最优解,数组中的第 i
个元素表示长度为 i
的绳子所得的当前最大值。在求 f(i)
前每一个 f(j)
都已经求出来了,并且结果已经保存到 vt
中,求解 f(i)
只需要求出所有的 f(j) * f(i - j)
并比较得到它们的最大值即可。
参见代码如下:
// 执行用时 :0 ms, 在所有 C++ 提交中击败了100.00%的用户
// 内存消耗 :8.6 MB, 在所有 C++ 提交中击败了100.00%的用户
class Solution {
public:
int cuttingRope(int n) {
vector<int> vt(n + 1, 0);
if (n == 2)
return 1;
if (n == 3)
return 2;
vt[0] = 0;
vt[1] = 1;
vt[2] = 2;
vt[3] = 3;
int max = 0;
for (int i = 4; i <= n; ++i) {
for (int j = 1; j <= i / 2; ++j) {
int tmp = vt[j] * vt[i - j];
if (max < tmp)
max = tmp;
}
vt[i] = max;
}
max = vt.back();
return max;
}
};
时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
空间复杂度:
O
(
n
)
O(n)
O(n)
对于这个问题来讲,有双 O ( 1 ) O(1) O(1) 的解法,见方法二。
方法二:数学、贪心、代码优化
针对:剪绳子 II
当 n ≥ 5
时,尽可能剪长度为 3 的绳子,当绳子长度为 4 时,就把绳子剪成两段长度为 2 的绳子。这样做为什么能得到正确答案?下面给出证明:
- 当
n ≥ 5
时,可通过式子展开简答证明2(n - 2) > n、3(n - 3) > n
这两个式子成立。也就是说当绳子长度大于等于 5 时,要尽可能将绳子剪成长度为 3、2 的绳子短。 - 并且能证明
2(n - 2) ≤ 3(n - 3)
,所以尽可能剪成长度为 3 的绳子段。
其实能发现长度为 4 的绳子可以不需要剪,因为其最大长度总为 4。这个方法同样适用于 剪绳子I
对于 剪绳子 II 还需注意数据溢出的情况,要么自行实现 pow()
函数,要么就采用 while
方法转换一下思路即可。
参见代码如下:
// 执行用时 :4 ms, 在所有 C++ 提交中击败了62.46%的用户
// 内存消耗 :8.2 MB, 在所有 C++ 提交中击败了100.00%的用户
class Solution {
public:
int cuttingRope(int n) {
if (n == 2)
return 1;
if (n == 3)
return 2;
int tmp3 = n / 3;
if (n - tmp3 * 3 == 1)
tmp3 -= 1;
int tmp2 = n - tmp3 * 3;
long long count2 = 1, count3 = 1;
while (tmp3) {
count3 *= 3;
count3 %= 1000000007;
--tmp3;
}
if (tmp2 == 0)
return count3;
count2 = tmp2 * count3;
if (count2 > 1000000007)
count2 %= 1000000007;
return count2;
}
};