JAVA算法:剪绳子获得最大乘积 — 动态算法
给定一根长度为N米的绳子,以最大化所有部分长度的乘积的方式,将绳子切割成不同部分的整数长度。你必须至少切一刀。
假设绳索长度超过2米。
例如:
输入: n = 2
输出: 1 (获得的最大乘积是: 1*1)
输入: n = 3
输出: 2 (获得的最大乘积是: 1*2)
输入: n = 4
输出: 4 (获得的最大乘积是: 2*2)
输入: n = 5
输出: 6 (获得的最大乘积是: 2*3)
输入: n = 10
输出: 36 (获得的最大乘积是: 3*3*4)
问题分析
最优子结构
这个问题类似于木棒切割问题。我们可以通过在不同位置进行切割并比较切割后获得的值来得到最大的积。对于切割后获得的工件,我们可以递归调用相同的函数。
假设maxprod(n)是长度n的绳子的最大积。maxprod(n)可以写如下:
maxProd(n) = max(i*(n-i), maxProdRec(n-i)*i) 其中 i 的取值范围为: {1, 2, 3 .. n}
重叠的子问题
下面是这个问题的简单递归实现。实现只遵循上面提到的递归结构。
package com.bean.algorithm.basic;
public class CutForMaxProduct {
// Java program to find maxium product
// The main function that returns
// maximum product obtainable from
// a rope of length n
static int maxProd(int n) {
// Base cases
if (n == 0 || n == 1)
return 0;
// Make a cut at different places
// and take the maximum of all
int max_val = 0;
for (int i = 1; i < n; i++)
max_val = Math.max(max_val, Math.max(i * (n - i), maxProd(n - i) * i));
// Return the maximum of all values
return max_val;
}
/* Driver program to test above functions */
public static void main(String[] args) {
System.out.println("Maximum Product is " + maxProd(10));
}
}
程序运行结果
Maximum Product is 36
在上述部分递归树中,MP(3)被求解两次。我们可以看到,有许多子问题是反复解决的。由于再次调用了相同的父问题,所以这个问题具有重叠子问题的特性。所以这个问题具有动态规划问题的两个性质。
与其他动态规划(DP)问题一样,可以通过自下而上构造dp[]来避免相同子问题重复调用计算的问题。
动态规划求解
package com.bean.algorithm.basic;
public class CutForMaxProduct3 {
// A Dynamic Programming solution for Max Product Problem
public static int maxProd(int n)
{
int val[] = new int[n+1];
val[0] = val[1] = 0;
// Build the table val[] in bottom up manner and return
// the last entry from the table
for (int i = 1; i <= n; i++)
{
int max_val = 0;
for (int j = 1; j <= i/2; j++)
max_val = Math.max(max_val, Math.max((i-j)*j, j*val[i-j]));
val[i] = max_val;
}
return val[n];
}
/* Driver program to test above functions */
public static void main(String[] args) {
System.out.println("Maximum Product is " + maxProd(10));
}
}
程序运行结果:
Maximum Product is 36
一个带有技巧的求解方法
如果我们仔细想想,分析这个问题的一些例子,可以很容易地观察到下面的规律:
当长度大于4时,可反复切割尺寸为3的子段,以获得最大乘积;
最后一个子段的长度为 2 或3 或4。例如:
当n=10时,为了获取最大乘积,子段的长度为3、3、4。
当n=11时,为了获取最大乘积,子段的长度为3、3、3、2。
下面是这个方法的代码设计:
package com.bean.algorithm.basic;
public class CutForMaxProduct2 {
/*
* Java program to find maximum product.
* The main function that returns the max
* possible product
*/
static int maxProd(int n) {
// n equals to 2 or 3 must be handled
// explicitly
if (n == 2 || n == 3)
return (n - 1);
// Keep removing parts of size 3
// while n is greater than 4
int res = 1;
while (n > 4) {
n -= 3;
// Keep multiplying 3 to res
res *= 3;
}
// The last part multiplied by
// previous parts
return (n * res);
}
/* Driver program to test above functions */
public static void main(String[] args) {
System.out.println("Maximum Product is " + maxProd(10));
}
}
程序运行结果:
Maximum Product is 36