参考自:《剑指Offer——名企面试官精讲典型编程题》
题目:剪绳子
给你一根长度为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) = max(f(i)*f(n-i)), 其中 0<i<n,从1到n/2遍历i,求出最大的f(n) 。为了避免重复计算,选择从下到上的顺序计算。
思路二:贪婪算法:当n大于等于5时,尽可能剪长度为3的绳子,当剩下长度为4时,把绳子剪成2段长度为2的绳子。
证明:n≥5时,2(n-2)>n, 3(n-3)>n, 并且3(n-3)≥2(n-2)
n=4时, 2×2>3×1
关键点:动态规划,贪婪算法
时间复杂度:动态规划:O(n^2);贪婪算法:O(1)
public class CutRope
{
public static void main(String[] args)
{
int length = 8;
int expected = 18;
int result = maxProductAfterCutting1(length);
System.out.println(result);
System.out.println(maxProductAfterCutting2(length));
}
/**
* 动态规划
*
* @param length
* @return
*/
private static int maxProductAfterCutting1(int length)
{
if (length < 2)
return 0;
if (length == 2)
return 1;
if (length == 3)
return 2;
int[] products = new int[length + 1];
products[0] = 0;
products[1] = 1;
products[2] = 2;
products[3] = 3;
int max;
for (int i = 4; i <= length; ++i)
{
max = 0;
//f(n) = max(f(i)*f(n-i)) 0<i<n
for (int j = 1; j <= i / 2; ++j)
{
int currentProduct = products[j] * products[i - j];
if (max < currentProduct)
max = currentProduct;
}
products[i] = max;
}
max = products[length];
return max;
}
/**
* 贪婪算法
* @param length
* @return
*/
private static int maxProductAfterCutting2(int length)
{
if(length < 2)
return 0;
if(length == 2)
return 1;
if(length == 3)
return 2;
// 尽可能多地减去长度为3的绳子段
int timesOf3 = length / 3;
// 当绳子最后剩下的长度为4的时候,不能再剪去长度为3的绳子段。
// 此时更好的方法是把绳子剪成长度为2的两段,因为2*2 > 3*1。
if(length - timesOf3 * 3 == 1)
timesOf3 -= 1;
int timesOf2 = (length - timesOf3 * 3) / 2;
return (int) (Math.pow(3, timesOf3)) * (int) (Math.pow(2, timesOf2));
}
}