程序基本算法习题解析 动态规划-乘积最大:设有一个长度为N的数字串,要求选手使用K个乘号将它分成K+1个部分,找出一种分法,使得这K+1个部分的乘积最大。

题目:

设有一个长度为N的数字串,要求选手使用K个乘号将它分成K+1个部分,找出一种分法,使得这K+1个部分的乘积最大。例如,数字串为312,当N=3,K=1时会有以下两种分法:3*12=36和31*2=62,符合题目要求的结果为31*2=62。程序的输入共有两行:第一行共有两个自然数N、K(6<=N<=40,1<=K<=6),第二行是一个长度为N的数字串,输出一个整数,表示求得的最大乘积。

思路:

定义一个数组dp[][],其中dp[i][j]表示从0到 i 的字符串中插入 j 个乘号得到的最大乘积。

例如需要在字符串 n1n2n3n4...nN中插入K个乘号,我们可以先将字符串从nk和nk+1中间分为两部分,如下图,nk+1 ~ nN作为一个整体,中间不插入乘号,则n0n0...nk之间需要插入K-1个乘号。

将n0n0...nk部分取出,按照之前的方式,从中间某个地方断开(插入一个乘号),后面部分作为一个整体,中间不插入乘号,例如,在n5和n6之间断开,则n0n1...n5之间需要插入K-2个乘号。

以此类推。

当倒推到某个地方,例如dp[i][j]时(角标为0~i 的子字符串,需要插入 j 个乘号),需要用到这样一个动态转移方程:

其中 k 表示从角标为 k 和 k+1 之间断开,num[k+1][i]表示断开后的后面部分数据(角标为 k+1  ~  i 的字符串),max括号中还有一个dp[i][j]表示之前求得的解,如有更优解,则更新,若新解不如之前的解,则不更新解。例如(如下图),数字串为1524643267,要求插入3个乘号(K=3)。

首先遍历 i (i < N),假设现在 i=6;再遍历 j(j < K),假设此时 j=2;即需要在子数字串1524643之间插入2个乘号,接着遍历 k(k< i),假设此时k=2,则152之间还需要插入一个乘号,此时

每个dp[i][j]都有一个初始值,后面根据动态转移方程进行更新,遍历完所有情况后,每个dp[i][j]中的值都是最优值。

代码如下:

// Chapter14_4.cpp : Defines the entry point for the application.
// 乘积最大
// 设有一个长度为N的数字串,要求选手使用K个乘号将它分成K+1个部分,找出一种分法,
// 使得这K+1个部分的乘积最大。例如,数字串为312,当N=3,K=1时会有以下两种分法:
// 3*12=36和31*2=62,符合题目要求的结果为31*2=62。
// 程序的输入共有两行:第一行共有两个自然数N、K(6<=N<=40,1<=K<=6),第二行是一个长度为N的数字串
// 输出一个整数,表示求得的最大乘积

#include "stdafx.h"
#include<iostream>
#include<string>
using namespace std;

int main()
{
	int K,N;
	char str[41]; //数字串
	int i,j,k;
	cout << "输入数字串的长度N:";
	cin >> N;
	cout << "输入乘号个数K:";
	cin >> K;
	cout << "输入数字串:";
	cin >> str;
	//dp[i][j]表示从角标0~i之间的数字串之间插入j个乘号得到的乘积
	int dp[41][7]; 
	//将dp的所有元素初始化为0
	memset(dp,0,sizeof(dp));
	//num[i][j]表示取角标i~j(包括i,j)之间的数字串
	int num[41][41];
	//将字符串转换为数字
	for(i=0;i<N;i++)
	{
		int temp = 0;
		for(j=i;j<N;j++)
		{
			temp = temp*10 + str[j] - '0';
			num[i][j] = temp;
		}
	}
	//当不插入乘号时,最大乘积为其本身
	for(i=0;i<N;i++)
		dp[i][0] = num[0][i];
	//动态转移方程,更新解
	for(i=0;i<N;i++)
		for(j=1;j<=K;j++)
			for(k=0;k<i;k++)
				dp[i][j] = max(dp[k][j-1]*num[k+1][i],dp[i][j]);
	//cout << "dp[i][j]:" << endl;
	//for(i=0;i<N;i++)
	//{
	//	for(j=0;j<=K;j++)
	//		cout << dp[i][j] << ' ';
	//	cout << endl;
	//}
	cout << "在插入" << K << "个乘号的情况下,数字串" << str << "的最大乘积为: " << dp[N-1][K] << endl;
	system("pause");
	return 0;
}

运行结果如下:

 

若是想查看在数字串的前 i 个子数字串中,插入 j 个乘号,求得的最大乘积dp[i][j],可把以上注释掉的代码放开,即

cout << "dp[i][j]:" << endl;
for(i=0;i<N;i++)
{
	for(j=0;j<=K;j++)
		cout << dp[i][j] << ' ';
	cout << endl;
}

运行结果如下: 

猜你喜欢

转载自blog.csdn.net/elma_tww/article/details/86522629
今日推荐