剑指offer-剪绳子 II

汇总:剑指offer算法合集

题目

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

答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。

示例 1:

输入: 2
输出: 1
解释: 2 = 1 + 1, 1 × 1 = 1

示例 2:

输入: 10
输出: 36
解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36

提示:

2 <= n <= 1000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jian-sheng-zi-ii-lcof

解题思路

  • 在之前有做过这道题的另一个版本,那个版本不存在大数越界要取模的情况,当时用了另外的方法,具体查看:剪绳子
  • 但在这题,我们需要使用另一种方式,为了在计算过程中可以正确取模。
  • 当某一段绳子剪段的时候,我们先列出各种情况:
    • 如果剪出一个长度为1的段,那这个段乘以哪个数都会是那个数本身,因此毫无意义,还不如把它加到别的长度上去,还能得到更大的乘积,因此剪出长度为1的段不可行
    • 如果剪出一个长度为2的段,看不出本身有什么问题,而且本身不能再拆分(拆分就是1了)
    • 如果剪出一个长度为3的段,也看不出本身有什么问题,这个段虽然可以再拆分为2和1,或者3个1,但前面说了,有长度1的拆分都是不满足要求的,所以这里不能拆分
    • 如果剪出一个长度为4的段,完全可以再拆分为两个2的段,而另外的拆法都含有1,所以只能拆成两个2,4 = 2 + 2 同时 4 = 2 * 2,因此4完全可以用2来替代,也就是剪成两个2结果不变,因此剪成长度4也没意义
    • 如果剪出一个长度4以上的段,他们都能拆分成2和3的长度组合(不包含1)。而一个数拆分成多个2或3后相乘,必定比它本身大,因此这些长度完全可以再拆分成2或3组成的多个段
  • 由上面分析可知,无论什么数,它的最大乘积的拆分段必定在这些2或3组成的段的某个组合里
  • 根据贪心算法,我们优先选择更大的数,这里我们优先选择3做乘积运算,直到最后剩余的长度小于等于4(因为小于等于4的时候如果再取3,那么可能会出现1,根据前面分析,剪长度1还不如把这个1放到别的长度上去乘积更大,因此4以下就不再取3了),我们分析以下小于等于4的情况
    • 剩余的长度等于4,那么我们可以用两个2来代替,但很明显,两个2相乘也等于4,所以直接乘上4本身结果一样
    • 剩余的长度等于3或2或1,都是不能再拆分的,直接乘上本身即可
    • 由上面的分析可知,当剩余长度小于等于4的时候。直接乘以这个长度即可

复杂度分析

循环剪绳子,时间复杂度为O(n),只用到了常数级变量,空间复杂度为O(1)

代码实现

class Solution {
    
    
    public int cuttingRope(int n) {
    
    
        //长度为2和3的时候,我们可以很轻易知道最大乘积
        if (n == 2) return 1;
        if (n == 3) return 2;
        //最大乘积,可能超出int的最大值,所以用long,初始值为1
        long pow = 1;
        //剩余长度大于4才继续循环
        while (n > 4) {
    
    
            //每次剪掉3的长度
            n -= 3;
            //把这个3做乘积运算,然后取模
            pow = pow * 3 % 1000000007;
        }
        //乘以剩余的长度,再取模得出最大乘积长度
        return (int) (pow * n % 1000000007);
    }
}

猜你喜欢

转载自blog.csdn.net/Nbin_Newby/article/details/121405441