Update 14. 剪绳子.md

调整题目描述与边界说明,精简贪心策略的推导过程,补充动态规划解法的状态定义与状态转移说明。
This commit is contained in:
CyC2018
2026-10-30 20:17:59 +08:00
parent af4df3331e
commit 4aa1511ede
+9 -13
View File
@@ -6,7 +6,7 @@
## 题目描述
把一根绳子剪成多段,并且使得每段的长度乘积最大。
把一根长度为 n 的绳子剪成多段(至少剪一刀,n ≥ 2),每段长度为整数,求各段长度乘积的最大值。
```text
n = 2
@@ -20,21 +20,15 @@ return 36 (10 = 3 + 3 + 4)
### 贪心
尽可能得多剪长度为 3 的绳子,并且不允许有长度为 1 的绳子出现。如果出现了,就从已经切好长度为 3 的绳子中拿出一段与长度为 1 的绳子重新组合,把它们切成两段长度为 2 的绳子。以下为证明过程。
结论:尽可能多剪长度为 3 的段;剩下 1 时,把一个 3 + 1 改成 2 + 2;剩下 2 时,单独留一段 2。例如 n = 10 = 3 + 3 + 4,乘积 3 × 3 × 4 = 36。
将绳子拆成 1 和 n-1,则 1(n-1)-n=-1\<0,即拆开后的乘积一定更小,所以不能出现长度为 1 的绳子。
推导分三步:
将绳子拆成 2 和 n-2,则 2(n-2)-n = n-4,在 n\>=4 时这样拆开能得到的乘积会比不拆更大。
- 不会出现长度为 1 的段:把 1 并到相邻的一段 x 上,乘积从 x 变为 x + 1,更大;
- 不会出现长度不小于 5 的段:把它拆成 2 和 x - 2,2(x - 2) - x = x - 4 \> 0,乘积更大。长度为 4 的段与 2 + 2 的乘积相同,可以看成两段 2,因此只剩下 2 和 3;
- 长度为 2 的段最多两段:三段 2 的总长与两段 3 相同,而 2 × 2 × 2 = 8 \< 3 × 3 = 9。
将绳子拆成 3 和 n-3,则 3(n-3)-n = 2n-9,在 n\>=5 时效果更好。
将绳子拆成 4 和 n-4,因为 4=2\*2,因此效果和拆成 2 一样。
将绳子拆成 5 和 n-5,因为 5=2+3,而 5\<2\*3,所以不能出现 5 的绳子,而是尽可能拆成 2 和 3。
将绳子拆成 6 和 n-6,因为 6=3+3,而 6\<3\*3,所以不能出现 6 的绳子,而是拆成 3 和 3。这里 6 同样可以拆成 6=2+2+2,但是 3(n - 3) - 2(n - 2) = n - 5 \>= 0,在 n\>=5 的情况下将绳子拆成 3 比拆成 2 效果更好。
继续拆成更大的绳子可以发现都比拆成 2 和 3 的效果更差,因此只考虑将绳子拆成 2 和 3,并且优先拆成 3,当拆到绳子长度 n 等于 4 时,也就是出现 3+1,此时只能拆成 2+2。
所以 n 除以 3 余 0 时全是 3;余 1 时最后是 2 + 2;余 2 时最后是一个 2。n = 2、3 时必须至少剪一刀,答案分别是 1 和 2,要单独处理。
```java
public int cuttingBamboo(int bamboo_len) {
@@ -54,6 +48,8 @@ public int cuttingBamboo(int bamboo_len) {
### 动态规划
dp[i] 表示长度为 i 的绳子剪过至少一刀后的最大乘积。枚举最后一段的长度 i - j,前面的 j 不再剪时乘积为 j × (i - j),继续剪时为 dp[j] × (i - j),取最大值。时间复杂度 O(N<sup>2</sup>)。
```java
public int cuttingBamboo(int bamboo_len) {
int[] dp = new int[bamboo_len + 1];