剪绳子
给你一根长度为
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
题解
-
暴力递归(超时)
- 剪绳子可以递归的分为两段,递归后代码中绳子的最大乘积可以是继续递归的值乘积,也可以是不再递归的乘积,比较两者的大小,去较大的。
-
动态规划
- 使用一个数组将前 长度的前 n-1 的绳子的最大乘积记录下来,如此循环,就可以找到 n 的最大乘积
- 时间复杂度:O(n*n)
- 空间复杂度:O(n)
class Solution { public int cuttingRope(int n) { if(n == 0)return 0; if(n == 1)return 0; if(n == 2)return 1; if(n == 3)return 2; int[] products = new int[n+1]; products[0] = 0; products[1] = 1; products[2] = 2; products[3] = 3; for(int i = 4; i <= n; i++){ int max = 0; for(int j = 1; j <= i/2; j++){ int product = products[j] * products[i-j]; int rawProduct = j * (i-j); int maxProduct = product > rawProduct ? product : rawProduct; if(max < maxProduct)max = maxProduct; } products[i] = max; } return products[n]; } }
-
贪婪算法
当n大于等于5时,我们尽可能多的剪长度为3的绳子;当剩下的绳子长度为4时,把绳子剪成两段长度为2的绳子。 为什么选2,3为最小的子问题?因为2,3包含于各个问题中,如果再往下剪得化,乘积就会变小。 为什么选长度为3?因为当n≥5时,3(n−3)≥2(n−2)3(n-3)≥2(n-2)3(n−3)≥2(n−2)
再解释一下为什么不选剪 4、5
剪 5 的情况可以再分为 2、3, 2*3>5
剪 4可以再分为 2、2, 2*2 >=4
所以不选剪 4、5
- 时间复杂度 O(1)
- 空间复杂度O(1)
class Solution { public int cuttingRope(int n) { if(n == 0)return 0; if(n == 1)return 0; if(n == 2)return 1; if(n == 3)return 2; int threeTime = n/3; int twotime = 0; if(n - threeTime * 3 == 1){ threeTime--; } twotime = (n - threeTime * 3) / 2; return (int)Math.pow(3,threeTime) * (int)Math.pow(2,twotime); } }
总结
动态规划一定可以暴力递归,先把暴力递归的方法找出来,再从下而上解决就是动态规划了。