剑指 Offer 每日一题 | 11、剪绳子

528 阅读3分钟

这是我参与8月更文挑战的第22天,活动详情查看:8月更文挑战

一、前言

大家好,本文章属于《剑指 Offer 每日一题》中的系列文章中的第 11 篇。

在该系列文章中我将通过刷题练手的方式来回顾一下数据结构与算法基础,同时也会通过博客的形式来分享自己的刷题历程。如果你刚好也有刷算法题的打算,可以互相鼓励学习。我的算法基础薄弱,希望通过这两三个月内的时间弥补这块的漏洞。本次使用的刷题语言为 Java ,预计后期刷第二遍的时候,会采用 Python 来完成。

二、题目

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

三、解题

3.1 思路1:动态规划

绳子被剪断必然至少分为两段,即长度为 n 的绳子必然在剪完第一刀之后,还有 n-1 的选择,因此 有 f(n) =max(f(i) * f(n-i)) 。

这是一个从上至下的递归公式,由于递归会有很多重复的子问题,从而造成大量不必要的重复计算。

因此,我们用一个数组 DP 来存放 这些长度的最大值。

  • DP[1] = 1
  • DP[2] = 1* 2 = 2
  • DP[3] = 1*3 = 3
  • DP[4] = 2*2 = 1 * 4 =4
  • DP[n] = DP[i] * DP [n-i]

不过,需要注意的是 如果绳子的长度小于 4,那它必须要被划分两刀,就达不到这几个 DP 值,因为它必须要划分,所以这几个值特殊处理下,然后在处理 长度大于等于4 的绳子。

3.1.1 代码

在这段代码中,子问题的最优解都存储在 DP 数组中 for 循环的 i 是顺序递增的,这意味着计算顺序是自下而上的。

    int [] dp =new int[n+1];
         if(n<=2)
             return 1;
         if(n==3)
             return 2;
         dp[1]=1;
         dp[2]=2;
         dp[3]=3;
         for (int i = 4; i <=n; i++) {
             for (int j = 1; j <=(i / 2); j++) {
                 dp[i]=Math.max(dp[i],dp[j]*dp[i-j]);
             }
         }
         return dp[n];
3.1.2 执行效果

image-20210822091127601

3.1.3 复杂度
  • 时间复杂度:O(n ^ 2)
  • 空间复杂度:O(n)

3.2 思路2:贪心算法

核心思路是 :当n >4 时尽可能的把绳子分为长度为3的小段,这样乘积最大。

数学证明,查看 k神:leetcode-cn.com/problems/ji…

  • 如果 n == 2,返回1,如果 n == 3,返回2,两个可以合并成n小于4的时候返回n - 1
  • 如果 n == 4,返回4
  • 如果 n > 4,分成尽可能多的长度为3的小段,每次循环长度n减去3,乘积res乘以3;最后返回时乘以小于等于4的最后一小段
  • 以上2和3可以合并
3.2.1 代码
 public class Number11_2 {
     public static void main(String[] args) {
         System.out.println(cuttingRope(5));
     }
     public static int cuttingRope(int n) {
         int [] dp =new int[n+1];
         if(n<4)
             return n-1;
         int res=1;
         while (n>4){
             res*=3;
             n-=3;
         }
         return res*n;
     }
 }
3.2.2 执行效果

image-20210822092530126

3.2.3 复杂度
  • 时间复杂度:O(n)O(n)
  • 空间复杂度:O(1)O(1)

四、总结

本题对动态规划和贪心算法进行了初步的考察,希望读者能够灵活运用动态规划问题来解决这样灵活问题,同时贪心算法也是需要数学基础的。