Leetcode 剑指 Offer 14- I/14- II. 剪绳子
题目
给你一根长度为 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:
输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1
示例 2:
输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36
思路
- 这道题目本质上就是在问下面的题设的答案:
已知n = k0 + k1 + k2 + ... + k(m - 1), 求k0 * ... * k(m - 1) 的最大值
- 这种带有限制条件的最值问题可以考虑使用高数之中的构造拉格朗日函数来解决
设F = k0 * k1 * ...k(m - 1) + t(k0 + k1 + ...k(m - 1) - n)
对k0求偏导, -》k1 * k2 * ... k(m - 1) + t = 0
......
对k(m - 1) 求偏导, -》k0 * k1 * ...k(m - 2) + t = 0
对t求偏导, -》k0 + k1 + k2 + ...k(m - 1) = n
容易解得当k0 == k1 == ... k(m - 1)的时候取得最大值
- 此时得到结论 —— 尽量剪每段一样长就可以达到最大值, 但是剪的段数是不确定的, 所以我们再设每段的长度为x, 在假设可以整除x的前提下, 即
设y = x * x * x ...x (此处x共有n / x个), 此时n可以看成一个固定的常数, 我们只用考虑x的(1/x)的次方
取对数求导可得 —— 当x等于e的时候(即2.7时候)可以取得最大值
- 但是题目要求分的必须为整数, 所以分别对2和3进行计算
2的(1/2)次方为1.414
3的(1/3)次方为1.442
- 所以我们每段分成长度为3即可
- 但是这道题目具有特殊的情况
在一段段剪3的长度之后, 最后剩下长度1, 2
长度为1的情况: 显然将之前的3和这个1分成另外的情况更优:3 * 1 < 2 * 2
长度为2的情况: 显然1 * 1 < 2即不需要再分
发现规律, 在最后剩下的长度小于等于4的时候, 就不用继续剪绳子了,因为该长度就是最大值的组成的部分
- 由于题目中要求绳子至少要剪成2段, 所以对于初始值n
n = 2: 1 * 1 = 1
n = 3: 1 * 2 = 2
代码
剪绳子I —— golang
func cuttingRope(n int) int {
if n < 4 {
return n - 1;
} else {
res := 1
for ;n > 4; {
res *= 3;
n -= 3;
}
return res * n;
}
}
剪绳子II —— golang
func cuttingRope(n int) int {
if n < 4 {
return n - 1;
} else {
res := 1
for ;n > 4; {
res = (res * 3) % 1000000007;
n -= 3;
}
return (res * n) % 1000000007;
}
}