题目
给你一根长度为 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
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jian-sheng-zi-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
解题方法
推荐视频:https://www.bilibili.com/video/BV1Nt4y1D7gh?from=search&seid=9284550418823896015
这道题跟数学有关:
当绳子长度为2的时候拆分成两个长度为1的绳子 它们的乘积为1
当绳子长度为3的时候可以拆分[1,1,1][1,2] 最大乘积为2
当绳子长度为4的时候可以拆分[1,1,1,1][1,3][1,2,1] 最大乘积为3
当绳子长度为5的时候可以拆分[1,1,1,1,1][1,2,2],[1,2,1,1],[2,3] 最大乘积为6
当绳子长度为6的时候可以拆分[3,3][3,2,1][2,4][1,1,4] 最大乘积为9
当绳子长度为7的时候可以拆分[1,6][3,3,1][3,4][2,5] 最大乘积为12
...
从上面的举例可以发现,想要乘积最大,需要尽量将绳子划分为长度3。
如果绳子长度 n % 3 = 0,也就是说绳子总长度刚好划分为每段长度为3的小绳子,那么最大的乘积就是 3^(n/3)
如果绳子长度 n % 3 = 1,也就是说绳子总长度划分为长度3的绳子后还剩1,
从上面举例绳子长度7可以发现,7划分为[3,3,1]显然没有[3,4]乘积大,
同理,我们就要把剩余的1和最后一段3的绳子合并,那么最大的乘积为 3^(n/3-1)*4
如果绳子长度 n % 3 = 2,也就是绳子总长度划分长度3后还剩2,
我们可以想:假设划分为[3,3,2][3,5][3,3,1,1]
我们可以看出[3,3,2]的乘积就是最大的了,如果和最后一段拼接为[3,5],乘积反而更小
所以如果绳子长度 n % 3 = 2,绳子的最大乘积就是 3^(n/3)*2
动态规划
贪心算法
数学
代码
// 数学
func cuttingRope(n int) int {
if n <= 3 {
return n-1
}
quotient := n/3
remainder := n%3
if remainder == 0{
return int(math.Pow(3,float64(quotient)))
}else if remainder == 1 {
return int(math.Pow(3,float64(quotient-1))) * 4
}
return int(math.Pow(3,float64(quotient))) * 2
}
// 贪心 时间复杂度O(n) 空间复杂度O(1)
func cuttingRope2(n int) int {
if n == 2{
return 1
}
if n == 3{
return 2
}
if n == 4{
return 4
}
result := 1
for n > 4{
result *= 3
n -= 3
}
result *= n
return result
}
// 动态规划 时间复杂度:O(n^2) 空间复杂度:O(n)
func cuttingRope3(n int) int {
dp := make([]int,n+1)
dp[2] = 1
max := func(a,b int) int {
if a > b{
return a
}
return b
}
// 外层循环为绳子长度,内层循环获取比较最大乘积
// 假如现在为长度10的绳子
// 指剪一刀划分为两根绳子:(i-j)*j: 9*1,8*2,7*3,6*4...
// 也可以根据前面绳子已经得到的最大乘积dp[i-j]*j: dp[9]*1,dp[8]*2,dp[7]*3...
// 然后比较这两者,获取最大乘积方案
for i := 3;i <= n;i++{
for j := 1;j < i;j++{
dp[i] = max(dp[i],max(j*(i-j),dp[i-j]*j))
}
}
return dp[n]
}