题目描述
输入一个长度为N的数字串, 用K个乘号将它分为 (K+1) 个部分,使得得到的乘积最大
例如N = 3 , K = 1,输入的数字串为 312
分法有两种
312 = 36
312 = 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;
}