题目
给定一长为n的绳子,要求把绳子剪成m段(m,n都是整数且n>1,m>1),每段绳子的长度记为k[0], k[1], k[2]…,k[m]。请问k[0]*k[1]*k[2]….*k[m]可能的最大乘积是多少?例如,当绳子的长度是8时,可以剪成2*3*3的三段得到最大的乘积。
解法
- 动态规划
从下往下分析问题,从下往上解决问题。先计算当绳子长度为2,3,4等这些较小的容易看出的最大值,再以此往上计算n为较大值时乘积的最大值。算法的时间复杂度是 O(n2) O ( n 2 ) 同时需要 O(n) O ( n ) 的空间复杂度。
#include <iostream>
using namespace std;
int maxProductAfterCutting(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; // n == 4的初始值
int max;
for(int i = 4; i <= length; ++i) {
max = 0;
// find the maximum among all possible value
for(int j = 1; j < i/2; ++j) {
int product = products[i] * products[i-j];
if(max < product) {
max = product;
}
products[i] = max;
}
}
max = products[length];
delete[] products;
return max;
}
贪心算法
从数学角度证明贪心策略的正确性,然后应用数学公式推导,以 O(1) O ( 1 ) 的时间复杂度和空间复杂度计算该问题的最优化值。数学证明:对与长度为n的绳子,当其剪为长度为k和n-k的两段绳子时,其乘积为 k(n−k) k ( n − k ) ,通过求导可以发现当k接近n/2时,其乘积最大。以此推及到n=4和n=5的情况(n<4的情况上述已经计算)。当n=4,则k=2时取得最大乘积;当n=5,则k=3时取得最大乘积。此时乘积为 3(n−3) 3 ( n − 3 ) 。因此当 n>=5 n >= 5 时,应该尽可能多地剪出长度为3的绳子段。(任何比5大的整数切分成两段的结果最终都会归结到绳子段长为3,4,5的情况)
int maxProductAfterCutting2(int length) {
if(length < 2) return 0;
if(length == 2) return 1;
if(length == 3) return 2;
// most cut of length 3
int timesOf3 = length/3;
// if remained length equals to 4
if(length - timesOf3 * 3 == 1)
timesOf3 -= 1;
int timeOf2 = (length - timesOf3*3)/2;
return (int)(pow(3,timesOf3)) * (int)(pow(2, timeOf2));
}