剑指Offer_#14-1_剪绳子

剑指Offer_#14-1_剪绳子

Contents

题目

给你一根长度为 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

思路分析

动态规划

设要求的最大的绳子长度乘积为f(n)
这是一个动态规划问题。动态规划问题包含四个特点:

  1. 目标是求一个问题的最优解
  2. 整体问题的最优解依赖于各个子问题的最优解
  3. 大问题分解若干个小问题,这些小问题之间包含重叠的子问题,例如
    • f(10)分解为f(4)和f(6)
    • f(4)分解为f(2)和f(2)
    • f(6)分解为f(4)和f(2)
      这当中f(2)既是f(4)的子问题,也是f(6)的子问题,所以我们说f(2)是一个重叠的子问题,所以为了减少复杂度,我们可以将f(2)记录下来。这样以后计算f(4)和f(6)时就不需要进行重复计算。
  4. 从上往下分析问题,从下往上求解问题
    • 从上往下分析问题。也就是说,我们都是从大问题出发,将其分解为一个个小问题,这是从上而下。但是由于上面所写的特点2,大问题的解决是依赖于小问题的,所以没办法从上往下的求解。例如:我们要求解问题f(8),是依赖于f(1),f(2)...,f(7)的,要从f(1)开始算起,算到f(7),而不可能从f(7)算起,算到f(1)。这是依赖关系导致的。
    • 从下往上求解问题。也就是先计算最小,最容易问题的最优解,再利用小问题最优解,解决大问题最优解。

解题思路

使用数组products[]记录f(n)的结果,下标对应着长度n(长度最小为2,索引0和1的元素仅仅是凑数的,目的是使得长度和下标可以相同)。
对于n<3的情况,可以直接得出:

  • 长度为2时,只能剪成1+1,f(2)=1
  • 长度为3时,只能剪成1+2,f(3)=2
    所以可以归结为f(n)=n-1(n<=3)。

当输入n>3时,就需要通过循环,计算出所有f(n),返回最后一个元素即可。

解答1

class Solution {
    public int cuttingRope(int n) {
        if (n <= 3) return n-1;//长度为2或3时,其实不切最好,但是题目要求必须至少切一刀,所以乘积反而减1
        int[] products = new int[n + 1];//products[i]是长度为i时问题的解。长度是n+1,最后一个元素索引为n。
        //如果n>3,最后剩下的长度小于3,应该选择不切
        products[0] = 0;
        products[1] = 1;
        products[2] = 2;
        products[3] = 3;
        //继续计算长度超过4的最大乘积,要依赖于0~3的结果(准确说是1~3的结果,0是凑数的)
        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];//从j的位置开始切割,计算最大乘积
                if(product > max) max = product;
            }
            products[i] = max;
        }
        return products[n];
    }
}

复杂度分析

空间复杂度

  • 使用一个数组来保存结果,

时间复杂度

  • 二层循环,

动态规划2

class Solution {
    public int cuttingRope(int n) {
        if(n == 2) return 1;
        if(n == 3) return 2;
        int[] dp = new int[n + 1];
        dp[0] = 0;
        dp[1] = 1;
        dp[2] = 1;
        dp[3] = 2;
        for(int i = 4; i <= n; i++){
            int max = 0;
            for(int j = 1; j < i; j++){//注意:主要是这里不同
                int product = Math.max(j * dp[i - j], j * (i - j));//还有这里
                if(product > max) max = product;
            }
            dp[i] = max;
        }
        return dp[n];
    }
}

解答2:贪心算法

  • n >= 5时,每次切出一段长度为3的绳子是最优的方法。即:每次使得绳子长度n减少3,结果res变为3倍。
  • n < 5时,剩下的绳子不需要继续切。即:把剩余的绳子长度直接乘到res里边。
class Solution {
    public int cuttingRope(int n) {
        if(n <= 3) return n - 1;
        int res = 1;
        while(n >= 5){
            res *= 3;
            n -= 3;
        }
        return res * n;
    }
}
posted @ 2020-06-18 18:06  Howfar's  阅读(142)  评论(0编辑  收藏  举报