最大K乘积问题

5.12.1题目描述

最大K乘积问题。设I是一个n位的十进制整数,如果将i划分为k段,则可得到k个整数。这k个整数的乘积称为I的一个k乘积。设计一算法解决K乘积问题。

5.12.2思路描述

分析该问题,首先其具有最优子结构,对于一个n位整数,划分k次,那么其乘积最大的划分情况一定是在划分k-1次时,再在第k次寻找一个使其乘积最大的划分。同时也能看出其具有重叠子问题性质,也就是我们所求的k次最优划分的方法依赖于第k-1次的划分方式。那么自然想到用动态规划解决。

根据分析的问题特点,设数组dp[i][k]代表对前i位数进行k次划分得到的最大乘积的值。number(x,i)代表从整数的第x位到第i位的整数值。那么递推公式就应为:

而对于划分结果输出,选择另设一个数组solve[i][k],记录在求得dp[i][k]的最优值时,其x的值,也就是此时的断点。根据该断点层层倒退,下一次个断点就应该位于求得dp[solve[i][k]][k-1]时的断点,以此类推得到所有断点,即可完成划分结果输出。

//dp(i,k)=max{dp(x,k-1)*number(x+1,i)} 
#include<iostream>
using namespace std;
int number[15];    
void save(int num,int n)   //输入的整数每一位存入数组number
{
	int b = 0;
	for (int i=n; i>=1; i--)
	{
		b = num % 10;
		num = num / 10;
		number[i] = b;
	}
	return;
}
int m(int i,int j)   //返回数组从i到j位组成的整数 
{
	int sum=0,time=1;  //time记录倍数,每一位*10s 
	for(int k=j;k>=i;k--)
	{
		sum=sum+time*number[k];
		time*=10;
	}
	return sum;
}


int main()
{
	int arr[15];         //记录整个划分的断点 
	int dp[15][15]={0};  //前i位被划分为k段时的最大乘积 
	int solve[15][15];   //记录对应断点位置 
	int n,k,num,last;
	cin>>n>>k;
	cin>>num; 
	save(num,n);
	if (k==1)   //分段数为1,直接输出 
	{
		cout<<"最大k乘积为"<<num<<endl;
		return 0;
	}
	for(int i=1;i<=n;i++)
		dp[i][1]=m(1,i);
	for(int i=1;i<=n;i++)
	{
		for(int j=2;j<=k;j++)
		{
			int max=0,t;       //t用来记录断点 
			for(int x=1;x<i;x++)     //最里层递归计算式,用于找到max 
			{
				int temp=0;
				temp=dp[x][j-1]*m(x+1,i);
				if(temp>max){
					max=temp;
					t=x;
				}
			}
			dp[i][j]=max;
			solve[i][j]=t;   //记录的是dp[i][j-1]再往后得到最大k乘的断点位置 
		}
	}
	cout<<"最大k乘积为:"<<dp[n][k]<<endl;
	arr[k]=solve[n][k];       //k次划分共k-1个断点 
	for(int j=k-1;j>1;j--)
		arr[j]=solve[arr[j+1]][j];    //倒推找断点,找到最后一个断点后根据断点去前面的数再划 
	cout<<"分解方式为:";
	for(int i=1,j=2;i<=n;i++)
	{
		cout<<number[i];
		if(i==arr[j])
		{
			cout<<"*";
			j++;
		}
	}

}

动态规划一般思路:

1.寻找问题的性质,最优子结构和重叠子问题。

2.确定dp数组的意义和维数。通常来说,应该根据上述的重叠子问题去考虑dp数组应该是怎么样的。

3.确定dp数组的递推关系。在这一步通常需要建立一个辅助数组或函数完成其他类的功能。

4.数组初始化。根据问题的性质,dp数组往往有几个初始的确定值,而后来的值就是根据初始值得到的,在这里一定要考虑所有可能的初始值,防止递推过程的错误。

5.进行多重循环求值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值