汇总:剑指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);
}
}