题目描述
给你一根长度为n的绳子,请把绳子剪成整数长的m段(m、n都是整数,n>1并且m>1,m<=n),每段绳子的长度记为k[1],...,k[m]。请问k[1]x...xk[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
思路
应用动态规划求解问题的特点:
- 题目是求一个问题的最优解(最大值或最小值);
- 该问题能分解成若干个子问题(整体问题的最优解依赖各个子问题的最优解);
- 子问题之间还有相互重叠的更小的子问题;
- 从上往下分析问题,从下往上求解问题(把已经解决的子问题的最优解存储到数组里,并把子问题的最优解组合起来逐步解决大问题)
- 每一步都可能面临若干个选择,只能把所有的可能都尝试一遍。
贪婪算法和动态规划不一样。应用贪婪算法解决问题时,每一步都可以做出一个贪婪的选择,基于这个选择,确定能够得到最优解。
思路1:动态规划。 (时间复杂度:O(n^2), 空间复杂度:O(n))
(1)计算顺序自下而上,在求f(i)之前,对于每一个j(0<i<j)而言,f(j)都已经求解出来了。
(2)求所有可能的f(j)*f(i-j),并比较得出它们的最大值。
思路2:贪婪算法。 (时间复杂度:O(1),空间复杂度:O(1))
按照如下的策略剪绳子,则得到的各段绳子的长度的乘积最大:
- 当n>=5时,尽可能多地剪长度为3的绳子;
- 当剩下的绳子长度为4时,把绳子剪成两段长度为2的绳子。
也就是
- 当n能被3整除时,乘积=3^(n/3);
- 当n除3余1时,这时候发现多了一个1,于是拿出前面的一个3,然后把这个1和前面那个3分解为2*2,所以乘积为 3^(n/3 - 1) * 4;
- 当n除3余2时,乘积为3^(n/3) * 2
☆☆解法1(动态规划)
public class Solution { //动态转移的方程为:dp[n] = dp[n - i] * dp[i],枚举i即可(1~n/2) public int cutRope(int target) { int[] dp = new int[target + 1]; // 0 ~ target if (target == 2) return 1; if (target == 3) return 2; dp[1] = 1; dp[2] = 2; dp[3] = 3; for (int i = 4; i <= target; i++) { for (int j = 1; j <= i / 2; j++) { dp[i] = Math.max(dp[i], dp[j]*dp[i-j]); } } return dp[target]; } }
Note: n<=3时,要分段。但是n>=4时,3就不要再分了,因为3分段要小于3,我们要记录最大的。
☆☆解法2(贪婪算法)
public class Solution { public int cutRope(int target) { if (target == 2) return 1; if (target == 3) return 2; if (target % 3 == 0){ return (int)Math.pow(3,target / 3); // 全3 }else if (target % 3 == 1){ return (int) Math.pow(3,target / 3 - 1) * 4; }else{ // target % 3 == 2 return (int) Math.pow(3, target / 3) * 2; } } }