算法要求
给你一根长度为n的绳子,请把绳子剪成m段(m、n都是整数,n> 1 并且m > 1),每段绳子的长度记为k[0],k[1],…k[m]。请问k[0]k[1]…k[m]可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2,3,3的三段,此时最大的乘积是18
思路
动态规划
- 首先定义函数f(n)为把长度为n的绳子剪成若干段后各段长度乘积的最大值。在剪第一刀的时候,我们有n-1种可能的选择,也就是剪出来的第一段绳子的可能长度分别为1,2,…n-1。因此f(n)= max(f(i)*f(n-i),其中0<i<n。
- 这是一个从上至下的递归公式。由于递归会有很多重复的子问题,从而有大量不必要的重复计算。
- 一个更好的办法是按照从下而上的顺序计算,也就是说我们先得到f(2)、f(3),再得到f(4)、f(5),直到得到f(n)。
- 例如:当绳子的长度为2时,只可能剪成长度都为1的两段,因此f(2)等于1;当绳子的长度为3时,可能把绳子剪成长度分别为1和2的两段或者长度都为1的三段,由于1×2>1×1×1,因此f(3)=2
另外一种解决的方案是:贪婪算法,也就是对某一个问题,希望得到最优解,我们先得到局部最优解,然后在得到全局最优解的一种算法。【此说法言简意赅,详情见百度】
- 由于任何一段绳子,如果长度大于3的话,那么剪成的每一段长度为2,或是3的话,可以使乘积为最大值,
- 先计算 绳子可以剪成长度为3 的有几段,之后计算可以剪成长度为2的有几段。
证明这种算法的正确性:当n >= 5 的时候,我们可以证明 2(n-2)> n 并且 3(n -3) > n。也就是说,当绳子大于5的时候,我们可以将绳子剪成2.或者3。而且当n >= 5的时候, 3(n -3) > 2(n-2),也就说,我们尽量剪成长度为3的绳子。之后在去剪长度为2的绳子。所得乘积或是最大的。
当长度为4的绳子,其实不剪绳子,此时乘积是最大的。但是题目要求至少要剪一刀。
算法实现
package com.offer.test;
/**
*
* 减绳子
*
* 给你一根长度为n的绳子,请把绳子剪成m段(m,n都是大于1 的整数)。每段绳子长度的可能最大乘积是多少。
* 使用 动态规划-贪婪算法
* @author zhouwenchen@021.com
* @date 2019年6月17日 下午2:24:13
*/
public class MaxProductAfterCuttingDemo {
/**
* 使用的是动态规划
* @param len
* @return
*/
public static int axProductAfterCutting(int len) {
if (len < 2) {
return 0;
}
if (len == 2) {
return 1;
}
if (len == 3) {
return 2;
}
int[] result = new int[len + 1];
result[0] = 0;
result[1] = 1;
result[2] = 2;
result[3] = 3;
// 自低向上开始求解
int max = 0;
for (int i = 4; i <= len; i++) {
max = 0;
for (int j = 0; j <= i / 2; j++) {
int tempResult = result[j] * result[i - j];
max = max < tempResult ? tempResult : max;
result[i] = max;
}
}
max = result[len];
return max;
}
/**
* 贪婪算法的实现
* @param len
* @return
*/
public static int maxProductWithGreedy(int len) {
if (len < 2) {
return 0;
}
if (len == 2) {
return 1;
}
if (len == 3) {
return 2;
}
// 长度为3的有几段
int timeOfThree = len / 3;
// 判断还剩下多少,进行判断
if (len - timeOfThree * 3 == 1) {
timeOfThree -= 1;
}
// 计算长度为2的有几段
int timeOfTwo = (len - timeOfThree * 3) / 2;
// 计算每段乘积的最大值
return (int) ((Math.pow(3, timeOfThree)) * (Math.pow(2, timeOfTwo)));
}
public static void main(String[] args) {
System.out.println(axProductAfterCutting(9));// axProductAfterCutting(8)=18
// System.out.println(maxProductWithGreedy(9)); // maxProductWithGreedy(8)=18
}
}