剑指 Offer 14- I. 剪绳子
难度中等568
给你一根长度为 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
提示:
2 <= n <= 58
题解:
类似这种求最优解、最大值、最小值、最短路径等的题目通常的情况下都是动态分配和贪心算法,关于动态分配,最主要的就是求出状态转移方程,而求出状态转移方程,首先就要明确对dp数组的定义。
在这道题目中,剪绳子,最少剪一次,因此m最小等于2,此时最大乘积就是(假设减去了i)i*(n-i),也就是说此时的绳子被分为两段,长度分别为i和n-i,对于剪下来的i和n-i,可以选择再次剪开或者不剪。这里就是我们的分歧点,不剪就保留原样,如果选择再次去剪
那么,假设长度为n的绳子被剪成若干段后,各段长度的最大乘积为f(n),选择剪的位置有很多,1、2、3...n-1(不能是n,否则意思就是剪到n,也就是这个绳子的长度,也就是没剪),此时的状态转移方程就是f(n)=max{f(i)*f(n-1)}(有点像一个递归),从上往下递归时可能会有很多重复计算,因此采用从下往上计算的方式,(本质也是循环求最大值),先计算f(2),f(3),后面的就方便依次计算了。f(2)=1*1=1,f(3)=1*2=2。
const n = 10;
var cuttingRope = function (n) {
let dp = new Array(n + 1).fill(0),
max;
dp[2] = 1;
for (let i = 2; i <= n; i++) {
//拆出来是0或者1都没有意义,所以从2开始
for (let j = 1; j < i; j++) {
max = Math.max(j * (i - j), dp[i - j] * j); //对比找到所得值最大的方法,dp[i-j]*j是一个递归运算,能够逐个计算出每种可能性,找到最大值
dp[i] = Math.max(dp[i], max); //dp[i]一直都是0,其实也就是把max赋给dp[i],循环给其赋值,最后得出的dp[n]就是要求得的最大值
}
}
return dp[n];
};
cuttingRope(n);