sword_for_offer/docs/剑指 Offer 14- I. 剪绳子.md
$$ n = n_1 + n_2 + ... + n_a $$
$$ \max(n_1 \times n_2 \times ... \times n_a) $$
以下数学推导总体分为两步:① 当所有绳段长度相等时,乘积最大。② 最优的绳段长度为 $3$ 。
$$ \frac{n_1 + n_2 + ... + n_a}{a} \geq \sqrt[a]{n_1 n_2 ... n_a} $$
推论一: 将绳子 以相等的长度等分为多段 ,得到的乘积最大。
$$ x^a = x^{\frac{n}{x}} = (x^{\frac{1}{x}})^n $$
$$ \begin{aligned} \ln y & = \frac{1}{x} \ln x & \text{取对数} \ \frac{1}{y} \dot {y} & = \frac{1}{x^2} - \frac{1}{x^2} \ln x & \text{对 $x$ 求导} \ & = \frac{1 - \ln x}{x^2} \ \dot {y} & = \frac{1 - \ln x}{x^2} x^{\frac{1}{x}} & \text{整理得} \end{aligned} $$
$$ \dot {y} \begin{cases}
0 & , x \in [- \infty, e) \ < 0 & , x \in (e, \infty] \ \end{cases} $$
$$ y(3) = 3^{1/3} \approx 1.44 \ y(2) = 2^{1/2} \approx 1.41 $$
$$ [y(3)]^6 = (3^{1/3})^6 = 9 \ [y(2)]^6 = (2^{1/2})^6 = 8 $$
推论二: 尽可能将绳子以长度 $3$ 等分为多段时,乘积最大。
{:width=600}
a 和 b 使用常数大小额外空间。Python 中常见有三种幂计算函数:
*和pow()的时间复杂度均为 $O(\log a)$ ;而math.pow()始终调用 C 库的pow()函数,其执行浮点取幂,时间复杂度为 $O(1)$ 。
class Solution:
def cuttingRope(self, n: int) -> int:
if n <= 3: return n - 1
a, b = n // 3, n % 3
if b == 0: return int(math.pow(3, a))
if b == 1: return int(math.pow(3, a - 1) * 4)
return int(math.pow(3, a) * 2)
class Solution {
public int cuttingRope(int n) {
if(n <= 3) return n - 1;
int a = n / 3, b = n % 3;
if(b == 0) return (int)Math.pow(3, a);
if(b == 1) return (int)Math.pow(3, a - 1) * 4;
return (int)Math.pow(3, a) * 2;
}
}
class Solution {
public:
int cuttingRope(int n) {
if(n <= 3) return n - 1;
int a = n / 3, b = n % 3;
if(b == 0) return pow(3, a);
if(b == 1) return pow(3, a - 1) * 4;
return pow(3, a) * 2;
}
};
数学推导需要一定的知识基础。下面分享一种基于贪心算法的思路,个人认为适合于时间有限情况下的快速解题。
设一绳子长度为 $n$ ( $n>1$ ),则其必可被切分为两段 $n=n_1+n_2$ 。 根据经验推测,切分的两数字乘积往往原数字更大,即往往有 $n_1 \times n_2 > n_1 + n_2 = n$ 。
- 例如绳子长度为 $6$ : $6 = 3 + 3 < 3 \times 3 = 9$ ;
- 也有少数反例,例如 $2$ : $2 = 1 + 1 > 1 \times 1 = 1$ 。
设一绳子长度为 $n$ ( $n>1$ ),切分为两段 $n=n_1+n_2$ ,切分为三段 $n=n_1+n_2+n_3$ 。 根据经验推测,三段 的乘积往往更大,即往往有 $n_1 n_2 n_3 > n_1 n_2$ 。
- 例如绳子长度为 $9$ : 两段 $9=4+5$ 和 三段 $9=3+3+3$,则有 $4 \times 5 < 3 \times 3 \times 3$ 。
- 也有少数反例,例如 $6$ : 两段 $6=3+3$ 和 三段 $6=2+2+2$,则有 $3 \times 3 > 2 \times 2 \times 2$ 。
总体上看,貌似长绳子切分为越多段乘积越大,但其实到某个长度分界点后,乘积到达最大值,就不应再切分了。 问题转化: 是否有优先级最高的长度 $x$ 存在?若有,则应该尽可能把绳子以 $x$ 长度切为多段,以获取最大乘积。
| 绳子切分方案 | 乘积 | 结论 |
|---|---|---|
| $2 = 1 + 1$ | $1 \times 1 = 1$ | $2$ 不应切分 |
| $3=1+2$ | $1 \times 2 = 2$ | $3$ 不应切分 |
| $4=2+2=1+3$ | $2 \times 2 = 4 > 1 \times 3 = 3$ | $4$ 和 $2$ 等价,且 $2+2$ 比 $1+3$ 更优 |
| $5=2+3=1+4$ | $2 \times 3 = 6 > 1 \times 4 = 4$ | $5$ 应切分为 $2+3$ |
| $6=3+3=2+2+2$ | $3 \times 3 = 9 > 2 \times 2 \times 2 = 8$ | $6$ 应切分为 $3+3$ ,进而推出 $3$ 比 $2$ 更优 |
| $>7$ | ... | 长绳(长度>7)可转化为多个短绳(长度1~6),因此肯定应切分 |