剑指67.剪绳子

题目描述

给你一根长度为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;
        }
    }
}
原文地址:https://www.cnblogs.com/HuangYJ/p/13673784.html