最大k乘积问题--动态规划

问题

问题描述:
设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{1
234,12
34,123
4}=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,36
4}=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<iostream>
#include<string>
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;
}`

输出结果测试:
在这里插入图片描述
在这里插入图片描述
添加:
(由于本人是编程小白第一次写blog,文章内容有些优化不足。并且该解法只能解决较简单的最大k乘积问题,不能解决较长的数据,等待更深入的学习后,作者会添加更优的解法,如果有其他大佬有更好的解法欢迎分享!)

  • 21
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值