动态规划之二:剪绳子问题

问题:现有一根长度为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;
	
}




猜你喜欢

转载自blog.csdn.net/zjx_cfbx/article/details/79951019