版权声明:写的不对的地方希望大家能“狠狠地”指出 https://blog.csdn.net/qq_37006625/article/details/84799916
最大k乘积问题
问题描述
设I是一个n位十进制整数。如果将I分割为k段,则可得到k个整数。这k个整数的乘积称为I的一个k乘积。试设计一个算法,对于给定的I和k,求出I的最大k乘积。
样例输入:
-
第一组
5 2
12345 -
第二组
5 3
4 5 6 2 1
样例输出
-
第一组
6170 -
第二组
12420
问题分析:
典型的区间动规问题
dp[i][j] 的意思就是前i位数,分成j段时的最优解
最优子结构:
dp[i][j] = dp[k][j-1] * m[k+1][i];
前i位数,分成j段时的最优解,可以看成前k位,分成j-1段时的最优解乘上最后i - k 位的值(m[k+1][j])的值。dp[k][j-1]就是这个问题的子问题。而且他又可以写成他的子问题乘的形式。
从上面的解释我们可以看出存在大量的重复子问题,但是由于算法是自底向上的,之前计算过的最优值已经被保存的数组里,当问题调用他的子问题的时候,只需取出数组中的数即可,免去了重复计算所带来的时间复杂度,这就是动规的精髓所在。
代码
/*
最大k乘积问题
设I是一个n位十进制整数。如果将I分割为k段,则可得到k个整数。这k个整数的乘积称为I的一个k乘积。
试设计一个算法,对于给定的I和k,求出I的最大k乘积。
*/
#include <cstdio>
#include <cstring>
#include <iostream>
#include<cstdio>
using namespace std;
#define MAX_N 110
static int dp[MAX_N][MAX_N];//从i开始往后j位数
static int m[MAX_N][MAX_N];//m数组里保存的是从第i位到第j位的数是多少
void dp1(int n)
{
for(int i=1;i<=n;i++)//初始化,前i位数字分一段就是第一位到第 i位表示的数
{
dp[i][1] = m[1][i];
}
for(int j=2;j<=n;j++)//分割段数
{
for(int i=j;i<=n;i++)//位数
{
for(int k=1;k<i;k++)
{
dp[i][j]=max(dp[i][j],dp[k][j-1]*m[k+1][i]);
}
}
}
}
int main()
{
int n,k;
cout << "请输入数字的位数 ,要分成的段数" << endl;
while(cin >> n >> k)
{
memset(m,0,MAX_N*MAX_N);
memset(dp,0,MAX_N*MAX_N);
cout << "请输入一个" << n << "位数" << endl;
for(int i=1;i<=n;i++)
{
char ch;
cin>>ch;
m[i][i]=ch-'0';
}
for(int i=1;i<=n;i++)
{
for(int j=i + 1;j<=n;j++)
{
m[i][j] = m[i][j-1]*10 + m[j][j];
}
}
dp1(n);
printf("%d位数分成%d段相乘所得到的最大值是: %d\n",n,k,dp[n][k]);
// cout << "最优值表" << endl;
// for(int i = 1;i <=n;i ++ ){
// for(int j = 1;j <= i;j ++){
// cout << dp[i][j] << " ";
// }
// cout << endl;
// }
cout << endl << "请输入n ,k" << endl;
}
return 0;
}