「题解」乘积最大

本文通过动态规划解决一个数学问题:给定一个数字串和乘号数量,如何将其分成多个部分以获得最大乘积。文章详细介绍了问题描述、输入输出格式、样例以及数据范围,并给出了分析和动态规划的解题思路,最后提供了相应的代码实现。

题目描述

 输入一个长度为N的数字串, 用K个乘号将它分为 (K+1) 个部分,使得得到的乘积最大
 例如N = 3 , K = 1,输入的数字串为 312
 分法有两种
 312 = 36
 31
2 = 62
 最大值为62

输入格式

 输入共两行
 第一行,正整数 N 和 K
 第二行,一个数字串

输出格式

 用K个乘号将数字串划分为(K+1)个部分所得到的最大乘积

样例

样例输入1

3 1
312

样例输出1

62

样例输入2

7 3
3314245

样例输出2

278040

数据范围与提示

2 <= N <= 30
1 <= K <= 10

分析

 定义f[l,r]表示区间l~r的乘积最大值肯定不合适,因为这样做没有合适的方法判断有多少乘号,所以我们只能定义f[i,l,r]表示将i个乘号插入区间[l,r]的最大乘积,但是这样做时间复杂度太高了,我们定义f[i,j]表示将i个乘号插入前j个数中的最大乘积,s[i,j]表示第i到j位所形成的数
 我们发现f[3,n]=max(f[2,n-1]*s[n,n],f[2,n-2]*s[n-1,n],…f[2,4]*s[5,n],f[2,3]*s[4,n])而f[2,n-1]=max(f[1,n-2]*s[n-1,n-1],f[1,n-3]*s[n-2,n-1]…f[1,3]*s[4,n-1],f[1,2]*s[3,n-1])
 以此类推,我们可以看到f[i,j]已经具有了最优子结构的特征,可以用动态规划求解

 初始化:f[0,j]=s[1,j]
 状态转移方程:f[i,j]=max(f[i,j],f[i-1,k]*s[k+1,j])[k=i~j-1]
 目标:f[k,n]

代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int M = 35;
int n, k;
int a[M];
long long f[M][M];

long long get(int st, int ed) { //即s[i,j]
	long long num = 0;
	for(int i = st; i <= ed; i ++) {
		num = num * 10 + a[i];
	}
	return num;
}

void Read(int len) {
	char x = getchar();
	while(x < '0' || x > '9') x = getchar();
	for(int i = 1; i <= len; i ++) {
		a[i] = (x - '0');
		x = getchar();
	}
} 

int main() {
	scanf("%d %d", &n, &k);
	Read(n);
	for(int i = 1; i <= n; i ++) {
		f[0][i] = get(1, i);
	}
	for(int i = 1; i <= k; i ++) {
		for(int j = i + 1; j <= n; j ++) {
			for(int q = i; q < j; q ++) {
				f[i][j] = max(f[i][j], f[i - 1][q] * get(q + 1, j));
			}
		}
	}
	printf("%lld", f[k][n]);
	return 0;
} 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值