一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第10天,点击查看活动详情。
题目:给定一根长为n的绳子,现要求将绳子剪成整数长度的m段,每段绳子的长度记为 k[0],k[1]...k[m-1] 。现求k[0]*k[1]*...*k[m-1]可能的最大乘积是。例如,当绳子的长度是8时,可以将它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
解题思路
在解本题之前,我们需要了解一个数学知识,即大于3的每个数我们都可以将其拆解为多个2和3的组合,例如对于数字4,我们可以将其拆解为两个2,而对于数字7,我们可以将其拆解为两个2和一个3。并且当一个数的拆解有多种组合时,例如数字8,我们可以拆解成5和3以及两个4,或者两个3一个2,那么可拆解长度长的乘积必然大于长度较短的。这点可以验证。
那么当绳子的长度大于三时,最终最大乘积必然由2和3组成,于是本题就转变成了如何将一个数尽可能多的转变成2和3的组合,可得代码如下:
public int cuttingRope(int n) {
if(n<=3) return n-1;
int divid = n / 3;
int di = n % 3;
if(di == 0){
return (int) Math.pow(3, divid);
}else if(di == 1){
return (int) Math.pow(3, divid-1)*4;
}else {
return (int) Math.pow(3, divid)*2;
}
}
而对于15题,也就是考虑了int类型的上界,我们需要对其取模,取模的方式有两种:
- 循环取模法
- 快速幂取模
需要了解的是,循环取模法利用的是,利用循环取模法得到的结果为:
public int cuttingRope(int n) {
if(n<=3) return n-1;
int divid = n / 3;
int di = n % 3;
long res = 1;
if(di == 0){
for(int i=0;i<divid;i++){
res = (res*3) % 1000000007;
}
return (int) res;
}else if(di == 1){
res *= 4;
for(int i=0;i<divid-1;i++){
res = (res*3) % 1000000007;
}
return (int) res;
}else {
res *= 2;
for(int i=0;i<divid;i++){
res = (res*3) % 1000000007;
}
return (int) res;
}
}
而快速幂取模则可以看这里: 快速幂取模快速算法超级详细介绍 和 快速幂取模 。
上述介绍的方法是比较数学的方法,较难想,我们再次思考,将一段绳子分成多段,假设我们先分开一段长度为i,则另一端长度为n-i,那么乘积最大值无非就是i*(n-i)或者对n-i再次分割,那么就可以使用动态规划来计算结果,我们假设动态转移方程为dp[i],代表的含义就是当绳子长度为i时可分割的乘积最大值,代码如下:
public int cuttingRope(int n) {
// dp[i] 正整数i拆分后的乘积最大值
int[] dp = new int[n + 1];
for(int i=2;i<=n;i++){
int curMax = i;
for(int j=0;j<i;j++){
curMax = Math.max(curMax, Math.max(j*dp[i-j], j*(i-j)));
}
dp[i] = curMax;
}
return dp[n];
}
此方法时间复杂度较高,为。