一道快手的算法面试题,没想到对数学要求还蛮高的!

点击上方“图解面试算法”,选择“星标”公众号

重磅干货,第一时间送达

大家好,我是景禹。

今天分享的题目来源于 LeetCode 上的剑指 Offer 系列 面试题14.I 剪绳子。根据统计,近期出现在快手的算法面试环节,属于中等难度,需要一定的数学功底。

题目链接:https://leetcode-cn.com/problems/jian-sheng-zi-lcof/

题目描述

给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且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 好 还是取 3 好呢?

时,等分的话,可以是 ,也可以是   ,乘积则分别对应 ,显然   ,所以当然是取 3 好了。

也就是说对于长度为 的绳子,我们尽可能以每一段为 3 进行分割,得到的乘积最大。

当绳子的长度 时,也就两种情况(因为题目中绳子的长度要求是

  1. ,只能拆为 ,乘积为 1 ;

  2. ,只能拆为 ,乘积为 2 ;

当绳子的长度 时,我们就可以直接用 对 3 进行求余运算,设商数为 ,余数为 ,即 ,其中余数又分为三种情况进行处理:

  1. 余数 ,此时直接返回 即为最大的乘积,比如 是,最大乘积为 时,最大乘积为 ;整除的情况都是如此。

  2. 余数 ,此时直接返回 是不对的,为什么呢?因为

    所以当 时,而是返回 . 比如 ,最大乘积为 ;当 ,最大乘积为 ;

  3. 余数 , 此时直接返回 即可。比如 ,最大乘积为 ;当 ,最大乘积为

实现代码
class Solution {
    public int cuttingRope(int n) {
        if(n <= 3){
            return n-1;
        }
        int a = n / 3;
        int b = n % 3;
        if(0 == b){
            return (int)Math.pow(3,a);
        }else if(1 == b){
            return (int)Math.pow(3,a-1) * 4;
        }else{
            return (int)Math.pow(3,a) * 2;
        }
    }
}
复杂度分析
  • 时间复杂度: ,实现中仅涉及取整、求余和求幂运算。

  • 空间复杂度:保存商数的变量 a 和保存余数的变量 b 仅使用常量空间。

二、动态规划

时,就分为两种情况:

  1. 时,绳子只能剪为两个长度 1 的绳子,最大乘积为 1;

  2. 时,绳子只能剪为长度为 2 和 1 的两段,最大乘积为 2;直接返回。

当 n > 3 时,就可以按照动态规划的逻辑进行处理了:

  1. 初始值为:

为什么这么设置呢?

因为对于 的绳子而言,我们完全可以拆分得到长度为 3、2 和 1 的绳子,拆分得到长度为 3 的绳子不必再拆分,算入乘积的话最大就是 3本身,2 和 1 同理。在举个栗子,比如 ,我们可以拆分为 ,而 3 、2 和 1 都是最终直接作为计算乘积时的因子出现的,所以 .

  1. 递推公式为:

对于长度为 的绳子而言,要取得最大乘积 ,就需要知道它的前 3 个状态 ,而相应的最大乘积分别为: 的最大乘积则取三者中的最大值。

动态规划的递推公式为:

动画演示
实现代码
class Solution {
    public int cuttingRope(int n) {
         if(n <= 3){
             return n-1;
         }
         int dp[] = {1,2,3};
         for(int i = 3; i < n; i++) {
          int tmp = Math.max(3 * dp[0],Math.max(2 * dp[1], 1 * dp[2]));
          dp[0] = dp[1];
          dp[1] = dp[2];
          dp[2] = tmp;
         }
         return dp[2];
    }
}
复杂度分析
  • 时间复杂度

  • 空间复杂度 ,使用的是长度为 3 的定长数组。

三、贪心方法

按照贪心策略来剪绳子,当 时,我们尽可能多地剪长度为 3 的绳子;当剩下的绳子长度为 4 时,把绳子剪成长度为 2 的两段绳子,因

实现代码
class Solution {
    public int cuttingRope(int n) {
      if (n <= 3) {
       return n-1;
      }

      int NumOf3 = n / 3;
      if (n - NumOf3 * 3 == 1) {
       NumOf3--;
      }
      
      int NumOf2 = (n - NumOf3 * 3) / 2;
      
      return (int)Math.pow(3, NumOf3) * (int)Math.pow(2, NumOf2);
    }
}
复杂度分析
  • 时间复杂度:

  • 空间复杂度:

知识点

贪心思想、动态规划、数学推导

---

由 五分钟学算法 原班人马打造的公众号:图解面试算法,现已正式上线!
接下来我们将会在该公众号上,为大家分享优质的算法解题思路,坚持每天一篇原创文章的输出,如果没有更新,说明我还在制作动画中^_^

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值