剪绳子
剪绳子
题目链接
题目描述
给你一根长度为 n 的绳子,请把绳子剪成整数长的 m 段( m 、 n 都是整数, n > 1 并且 m > 1 , m <= n ),每段绳子的长度记为 k[1],...,k[m] 。请问 k[1]k[2]...*k[m] 可能的最大乘积是多少?例如,当绳子的长度是 8 时,我们把它剪成长度分别为 2、3、3 的三段,此时得到的最大乘积是 18 。
数据范围: 2 <= n <= 60
进阶:空间复杂度 O(1) ,时间复杂度 O(n)实例1
输入:
8
返回值:
18
说明:8 = 2 +3 +3 , 2*3*3=18
实例2
2
返回值:
1
2>1,所以切成两段长度是1的绳子
题目分析
暴力递归
分析
一、递归函数的设计和功能:back_track(n); 含义是:求长度为n的数,最后分段后的最大乘积,这里我们不需要关心分成多少段
二、递归函数的终止条件: 如果n <= 4, 显然back_track(n) = n,初始条件也就是我们不用计算就能得到的。
三、下一步递归:对于长度n,我们需要减少递归参数n,如果第一段为1, 显然下一步递归为back_track(n-1),如果第一段为2, 则下一步递归为 back_track(n-2)...因为要至少分2段,所以,最后一次可能的情况为最后一段为n-1, 下一步递归为back_track(1), 因此,每一步可能的结果为1 * back_track(n-1), 2 * back_track(n-2), ..., (n-1) * back_track(1),在n-1种情况中取一个最大值即可。这里我们不用关系back_track(n-1)等的值为多少,因为最终会递归到我们的终止条件,因此绝对是可以求出来。
public static int cutRope(int target) {
// 2和3 分段的话,分2段和一段的结果是不一样的
if (target == 2) {
return 1;
} else if (target == 3) {
return 2;
}
return back_track(target);
}
private static int back_track(int target) {
//
if (target <= 4) {
return target;
}
int ret = 0;
for (int i = 1; i < target; i++) {
ret = Math.max(ret, i * back_track(target - i));
}
return ret;
}
数学方式
分析: 在做这题之前我们先来看这样一个问题,一个整数先把他分成两部分,x+y=n(假设x>=y并且x-y<=1,也就是说x和y非常接近)那么乘积是xy。然后我们再把这两部分的差放大(x+1)+(y-1)=n(假设x>=y);他们的乘积是(x+1)(y-1)=xy-(x-y)-1,很明显是小于xy的,所以我们得出结论,如果把整数n分为两部分,那么这两部分的值相差越小乘积越大。
SO
/**
* 数学方式的解法
* @param target
* @return
*/
public static int cutRope2(int target) {
if(target == 2 || target == 3) {
return target - 1;
}
int ret = 1;
while (target > 4) {
target = target - 3;
ret = ret * 3;
}
return target * ret;
}