动规之最大k乘积问题

版权声明:写的不对的地方希望大家能“狠狠地”指出 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;
}

猜你喜欢

转载自blog.csdn.net/qq_37006625/article/details/84799916