14- I. 剪绳子
题目:给你一根长度为 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
方法一: 动态规划
主要思路:在已知长度为n的绳子最大乘积的基础上,长度n+1绳子的最大乘积可能来源依赖于前面已知求解。
定义辅助数据结构:记长度为n的绳子最大乘积为s[n],简单起见,不考虑索引偏移问题,初始化s = [1]*(n+1)。
假设当前处理长度为i的绳子最大乘积问题:
(1)从i中截取一块长度为j的绳子,剩余长度i-j的最大乘积为s[i-j],即最后一段长度为j的方案带来的乘积为j*s[i-j];
(2)对j从1到n-1进行遍历,取最大值;
(3)由于我们假定长度i-j绳子最大乘积为s[i-j]的前提是要求i-j必须进行分割(m>1),然而对于从i中截取j这一事件本身已经满足分割要求,所以上述取最值还需增加一个补丁:即仅对长度i进行i-j和j两段分割的判断。
class Solution:
def cuttingRope(self, n: int) -> int:
s = [1]*(n+1)
for i in range(2, n+1):
for j in range(1, i):
s[i] = max(s[i], s[j]*(i-j), j*(i-j))
return s[n]
方法二:数学公式推导
推论:尽可能将绳子以长度3等分为多段时,乘积最大!
切分规则:
(1)最优: 3 。把绳子尽可能切为多个长度为 3 的片段,留下的最后一段绳子的长度可能为 0,1,2三种情况。
(2)次优: 2 。若最后一段绳子长度为 2 ;则保留,不再拆为 1+1 。
(3)最差: 1 。若最后一段绳子长度为 1 ;则应把一份 3 + 1 替换为 2 + 2,因为 2×2 > 3×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)) #留下的最后一段绳子的长度为 0 if b == 1: return int(math.pow(3, a - 1) * 4) #留下的最后一段绳子的长度为 1
return int(math.pow(3, a) * 2)