问题:现有一根长度为N的绳子,需要你剪成M段,使M段的乘积最大。(其中M、N都为整数,剪成的每段长度也为整数,N已知,M未知)
例如 绳子长度N=8 剪成M=3,数值为别为2,3,3,则乘积最大为 2*3*3=18。
当我们遇到一个大规模问题时,总是习惯把问题的规模变小,这样便于分析讨论。
我们从最简单的情况进行分析:
当绳子的长度N=1时,我们至少需要剪绳子一次,这时乘积为0;
当绳子的长度为N=2时,剪一次后绳子的两段长度为分别为1、1,这时乘积为1*1=1;
当绳子的长度为N=3时,我们对绳子有两种方式,其一为剪成三段,它们的长度分别为1、1、1,乘积为1*1*1=1;其二为剪成两段,它们的长度分别为1、2,乘积为1*2=2,则最终乘积取这两种方式中的最大值为max(1*1*1,1*2)。
当绳子的长度为N=4时,我们对绳子有三种方式,其一为剪成四段,它们的长度分别为1、1、1、1.乘积为1*1*1*1=1;其二为剪成两段,他们的长度为1、3.乘积为1*3=3,其三为剪成三段,它们的长度分别为1、2、1,乘积为1*2*1=2;其四为剪成两段,它们的长度分别为2、2,乘积为2*2=4;则最终乘积取上面结果中最大值max(1*1*1*1,1*3,1*2*1, 1*2)。
现在我们要用数学符号表示剪绳子的最大乘积的状态,使用f(N)表示绳子长度为N剪成若干段的最大乘积状态,由上面分析可知,
当N=1时,f(1)=0;
当N=2时,f(2)=1*1=1;
当N=3时,f(3)=max(1*1*1,1*2)=2
当N=4时,f(4)=max(1*1*1*1,1*3,1*2*1, 1*2)=4
我们知道使用动态规划求解问题,需要具备其中一个性质:最优子结构性质,也即我们需要知道状态转移函数。通过对上述状态的描述我们可以进一步简化中间环节:
当N=1时,f(1)=0;
当N=2时,f(2)=1;
当N=3时,f(3)=f(1)*f(2)=2
当N=4时,f(4)=max(f(1)*f(3),f(2)*f(2))=4
当N=5时,f(5)=max(f(1)*f(4),f(2)*f(3))=6
…….
f(N)=max(f(1)*f(N-1), f(2)*f(N-2) , f(3)*f(N-3) , … , f(i)*f(N-i) )
由此,我们可以知道状态转移函数为:f(N)= max( f(i) * f(N-i) ) , 其中i的取值范围为(i>0&&i<=n/2)。
程序如下所示:
#include<iostream>
using namespace std;
#define size 100 //表格的大小
int Line_Max(int a[],int n) {
if (n < 2)
return 0;
if (n == 2)
return 1;
if (n == 3)
return 2;
for (int i = 4; i <= n; i++)
for(int j=1;j<=i/2;j++) //比较的范围为:(1,n/2)
if (a[j]*a[i-j]>a[i])
a[i] = a[j] * a[i - j];
return a[n];
}
int main() {
int table[size]= { 0,1,2,3 };//对表格进行初始化,用来存储长度为i的最大乘积值
int n;
cin >> n;
cout << Line_Max(table, n) << endl;
}