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.进行多重循环求值。