剑指 Offer 14- I. 剪绳子 - Touale Cula's Blog

题目内容

给你一根长度为 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:

1
2
3
输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1

示例 2:

1
2
3
输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36

提示:

1
2 <= n <= 58

解法一:动态规划

思路

对于的长度为n的绳子,当 n ≥ 2 时,可以剪成至少两个绳子。令 k 是剪出的第一段绳子,则剩下的部分是 n−k,n−k 可以不继续剪,或者继续剪成至少两段绳子

算法

dp[i] 表示将长度为 i 的绳子剪成至少两段绳子之后,这些绳子长度的最大乘积

当 i ≥ 2 时,假设对长度为 i 绳子剪出的第一段绳子长度是 j(1≤j<i),则有以下两种方案:

  • 将 i 剪成 j 和 i-j 长度的绳子,且 i−j 不再继续剪,此时的乘积是 j×(i−j) ;
  • 将 i 剪成 j 和 i−j 长度的绳子,且 i−j 继续剪成多段长度的绳子,此时的乘积是 j×dp[i−j] 。

因此,当 j 固定时,有 dp[i]=max(j×(i−j),j×dp[i−j])。由于 j 的取值范围是 1 到 i ,需要遍历所有的 j 得到dp[i]的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public:
int cuttingRope(int n) {

if(n == 2)return 1;
vector<int> dp(n+1);
dp[2] = 1;
for(int i = 3;i<=n;i++){
for(int j=2;j<i;j++){
dp[i] = max(dp[i] ,max(j*(i-j),dp[i-j] * j ));
}
}
return dp[n];
}
};
1
2
3
4
5
6
7
8
9
10
11
12
执行用时:
0 ms
, 在所有 C++ 提交中击败了
100.00%
的用户
内存消耗:
6 MB
, 在所有 C++ 提交中击败了
71.06%
的用户
通过测试用例:
50 / 50

解法二:贪心算法

思路
核心思路是:尽可能把绳子分成 长度为3的小段,这样乘积最大。

算法

  • 如果 n === 4,返回4
  • 如果 n > 4,分成尽可能多的长度为3的小段,每次循环长度n减去3,乘积res乘以3;最后返回时乘以小于等于4的最后一小段
1
2
3
4
5
6
7
8
9
10
11
12
13
class Solution {
public:
int cuttingRope(int n) {

if(n<4)return n-1;
int res = 1;
while(n>4){
res*=3;
n -= 3;
}
return res*n;
}
};
1
2
3
4
5
6
7
8
9
10
11
12
执行用时:
0 ms
, 在所有 C++ 提交中击败了
100.00%
的用户
内存消耗:
5.9 MB
, 在所有 C++ 提交中击败了
83.55%
的用户
通过测试用例:
50 / 50