题目
现有一根长度为N的绳子,需要你剪成M段,使M段的乘积最大。(其中M、N都为整数,剪成的每段长度也为整数,N已知,M未知)。例如长度为8的绳子,当剪为3段乘积最大,即2*3*3=18.
思路
看到这种求最优解的题型,你就应该思考一下动态规划是否适合。这个绳子我可以一次一次的剪,第一次剪成两段,这就变成两根新绳子,只要我分别知道这两根新绳子最大的乘积,那么我就知道了整条绳子的最大乘积了,这就将一个问题,划分为两个子问题了,且各子问题之间相互独立,满足最优子结构,因此可以使用动态规划
首先确定边界条件和状态转移方程:
- 当绳子长度为1时,最大乘积为0
- 当绳子长度为2时,可以剪成1*1,最大乘积为1
- 当绳子长度为3时,可以剪成(1*2,1*1*1),最大乘积为2
- 当绳子长度为4时,可以剪成(1*1*1*1, 1*2*1, 2*2, 1*3),最大乘积为4
- 当绳子长度为5时,可以剪成(1*1*1*1*1, 1*2*2, 3*2, 1*2*1*1, 1*3*1,1*4),最大乘积为6
我们可以看到,当绳子长度n大于等于4时,f(n) = max( f(i) * f(n-i) )
,其中1 < i <= [n/2]
,因此我们可以用遍历来实现状态转移方程
代码
function LineMax(n){
if (n<=1) return 0;
if (n==2) return 1;
if (n==3) return 2;
const a =[0,1,2,3]; //排出前面的边界条件,第i项表示长度为i的绳子的最大乘积
const b =[[0],[1],[2],[3]]; //第i项表示,长度为i绳子的最大乘积组合
for (var i=4;i<=n;i++){
a[i] = 0; //初始化
for (var j=1;j<= n/2;j++){ //循环找出最大乘积及组合并分别记录在a,b数组中
if (a[j]*a[i-j] >a[i]){
a[i] = a[j]*a[i-j];
b[i] = [...b[j], ...b[i-j]];
}
}
}
console.log(a[n]); //输出长度为n的绳子最大乘积
console.log(b[n]); //输出长度为n的绳子最大乘积时的划分组合
}