问题 问题描述: 设x是一个n位十进制整数。如果将x划分为k段,则可得到k个整数。这k个整数的乘积称为x的一个k乘积。试设计一个算法,对于给定的x和k,求出x的最大k乘积。

编程任务: 对于给定的x和k,编程计算x的最大k 乘积。

示例 Sample Input: 请输入整数位数n:4 请输入整数划分位数k:3 请输入4位整数x:1234

Sample Output: 1234的k-1次分割后的最大k乘积是:144

推导过程 将一个n位的数字划分为k段,注意到分为k段是一个多阶段决策测问题,应采用动态规划算法来求解是适宜的。

推导打表过程:

(假设输入4位整数1234划分为3段求最大乘积数) 第一列:表示整数只分一段,结果就是本身。 第二列:表示整数分为两段。 f(2,2)=max{f(1,1)num(1,2)}= max{12}=2; f(3,2)=max{f(1,1)num(2,3),f(2,1)num(3,3)}=max{123,123}=36; f(4,2)=max{f(1,1)num(2,3),f(2,1)num(3,4),f(3,1)num(4,4)}= max{1234,1234,1234}=492; 第三列:表示整数分三段。 f(3,3)=max{f(2,2)num(3,3)}=max{123}=6; f(4,3)=max{f(2,2)num(3,4),f(3,2)num(4,4)}=max{1234,364}=144;

状态转移方程: f(i,j):表示前i个数分为j段后的最大乘积数。num(i,j)表示第i位开始到第j位的数。一般为了求取f(i,j),求数前i位,设前h个数中已经分成了j-1段,此时最大乘积为(h,j-1)*num(j+1,i)。f(i,j)=max{f(h,j-1)*num(j+1,i)} (j-1<=h<=i-1)

边界条件: 前面i个数字没有进行划分是值显然为前i个数字组成的整数,因而得到边界值为: f(i,1)=num(1,i) (1<=i<=n)

打印结果乘号: 为了能够打印相应的插入乘号的乘积式,设置标注位置的数组t[j]和c[i][j],其中c[i][j]是为了相应的f[i][j]的第j-1个划分点的位置,而t[j]表明了第j-1个乘号的位置。 当给数组赋值f[i][j]=f[h][j-1]*num(h+1,i)时,做相应赋值c[i][j-1]=h,表明f[i][j]的第j-1个乘号的位置时h。在求得f[n][k]时第k-1个乘号的位置t[k-1]=c[n][k-1]=h的基础上,其他tj可应用t[j]=c[t[j+1]][j]逆推产生。

具体案例实现流程 利用notability实现推导6位数字765438划分为4段求解最大乘积问题

代码实现: c++代码:(重点代码有标注**–**)


#include
using namespace std;
string x;//全局变量
int num(int i,int j)//是为了截取从i开始的长度为j的数字
{
int sum=0;
for(int k=i;k<=j;k++)//将字符串转换为相对应的数,如字符串123->数123
{
sum=sum*10+(x[k-1]-'0');
}
return sum;//返回字符串中所截取到的数
}
int main()
{
int n,k;//初始条件输入
cout<<"请输入整数位数n:";
cin>>n;
cout<<"请输入整数划分位数k:";
cin>>k;		//n位数被分为k段
cout<<"请输入"<<n<<"位整数x:";
cin>>x;		//输入的数
int f[n+1][k+1];//f[i][j]: 数字的前i位数被分为j段所得到的最大乘积  1/2/34
int i,j,h;
int t[k];//建立划分点
int c[n][k];
//初始化 
for(i=1;i<=n;i++)//表示是第一列的内容,数只分为一段,于是这部分的数就是当前内容
{
	for(j=1;j<=k;j++)
	{
		f[i][j]=0;
	}
	**f[i][1]=num(1,i);//边界条件** 
}
//这段 j=1 是不将数字分段,就是前i位数
//num(1,i)是指截取从第0位到第i-1位的数 
//(1234:f[1][1]=1,f[2][1]=12,f[3][1]=123,f[4][1]=1234)

for(j=2;j<=k;j++)//分为j段
{
	for(i=j;i<=n;i++)//前i位数 
	{
		 
		for(h=j-1;h<i;h++)//h为划分点,这是在对前面i个数进行划分,这也只是划分点数的位置,并非是数 
		{
			//f[h][j-1]*num(h+1,i)=前h个数被分为j-1段最大的乘积 *第j段的数(也就是没有并入前h个数的i-h个数) 
			if(f[i][j]<f[h][j-1]*num(h+1,i))//这里是在判断前面 
			{
				**f[i][j]=f[h][j-1]*num(h+1,i)**;//记录当前位置的最大值 
				**c[i][j-1]=h;**	//记录当前求出最大值的断点位置,也就是插入乘号的位置,这个位置是数	
			}
		}
	}	
}

t[k-1]=c[n][k-1];//记录当前倒数第一个断点位置 
for(j=k-2;j>=1;j--)//逆推出第j个乘号的位置t[j] 
{
	**t[j]=c[t[j+1]][j];**//这是在利用后一个断点位置找出前一个断点位置 
}
t[0]=0;//t数组没有插入断点,此处赋值是为了后面输出结果 
t[k]=n;//t数组的k位没有插入断点,此处赋值是为了后面输出结果 
cout<<endl;

cout<<"划分过程乘积最优子结果内容如下(打表):"<<endl;
cout<<"i\\j\t";
for(int z=1;z<=k;z++)
{
	cout<<z<<"\t";
} 
cout<<endl;

for(i=1;i<=n;i++)//输出打表内容,求出最优解 
{
	cout<<" "<<i<<"\t";
	for(j=1;j<=k;j++)
	{
		cout<<f[i][j]<<"\t";
	}
	cout<<endl;
}	
cout<<endl;

cout<<x<<"分成"<<k<<"段后的最大k乘积是:";
for(j=1;j<=k;j++)//输出最优值的分割内容 
{	
	**for(int u=t[j-1]+1;u<=t[j];u++)**//输出从一个断点到另一个断点中间的数 
	{
		cout<<x[u-1];	
	}
	if(j<k)//输出乘号做分割 
	{
		cout<<"*";
	}
}
//输出最优值 
cout<<"="<<f[n][k]<<endl;
}