一、题目描述
题目来自剑指Offer 14-I ,难度中等。
给你一根长度为 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
提示:
2 <= n <= 58
二、题目解析
- 长度为n的绳子,切成m段,要求这m段的最大乘积,可以先从长度比n小的绳子转移来。先算出长度为2的绳子的最大乘积,再算长度为3的,以此类推,直到长度为n。
- 用一个dp数组记录 从2到n长度的绳子剪掉后的最大乘积。我们设 i 为当前的绳子长度,dp [ i ] 为绳子乘积的最大值。因为初始dp[2]=1,那么 i 从长度为3的绳子开始,取值范围为 [ 3 , n ] 。
- 对长度为 i 的绳子,先剪掉第一段长度为 j ,那么剩下的绳子长度为( i - j )。
- 对于剪掉的第一段绳子 j , 它的长度一定不是1,因为1对后面的乘积没有任何增益效果,所以 j 取值范围为 [ 2 , i )。
- 对于剩下的绳子 i - j ,它可以剪也可以不剪。
如果不剪的话,那么乘积为 j * ( i - j)。
如果剪的话,那么乘积为 j * dp[ i - j ]。
取两中情况的最大值,即 max( j * ( i - j), j * dp[ i - j ] )。 - 对长度为 i 的绳子,对其所有 j 不同的情况取最大值,因此可得到 dp[ i ]转移方程为
dp [ i ] = max( dp [ i ] , max( j * ( i - j), j * dp[ i - j ] )。 - 最后返回 dp [ n ] 即可。
三、参考代码
class Solution:
def cuttingRope(self, n: int) -> int:
#用一个名为 dp 的数组来记录从 0 到 n 长度的绳子剪掉后的最大乘积
#默认都是0
dp = [0 for _ in range(n+1)]
#由于绳子必须被剪,所以长度为 2 的绳子只有剪成1和1两段,乘积结果为1
dp[2] = 1
#从长度大于等于3的绳子才开始进入讨论范围
#从长度为3的绳子开始考虑,讨论它的最大乘积是多少,并不断延伸绳子长度
for i in range(3,n+1):
#对于长度为i的绳子,它可以分为两个区间 j 和 i-j
#j的范围由2开始,因为剪成1的绳子无法扩大乘积的值
#j可以延伸到i-1
for j in range(2,i):
#不剪总长度乘积为 j*(i-j)
#剪的话长度乘积为 j*dp[i-j]
#取两者的最大值,即 Max(j*(i-j),j*dp[i-j])
#那么此时 dp[i]的值取i不剪的值dp[i]和剪的值Max(j*(i-j),j*dp[i-j])这两者的最大值
#而j有不同种情况,则每次的dp[i]会不同
#状态转移方程为dp[i] = max(dp[i],max(j*(i-j),j*dp[i-j]))
#比如一开始,i=3,j=2
#dp[3]=max[0,max(2*1,2*dp[1])]
# =max[0,max(2,2)]
# =2
dp[i] = max(dp[i],max(j*(i-j),j*dp[i-j]))
return dp[n]