整数划分(四)
时间限制:1000 ms | 内存限制:65535 KB
难度:3
描述
暑假来了,hrdv 又要留学校在参加ACM集训了,集训的生活非常Happy(ps:你懂得),可是他最近遇到了一个难题,让他百思不得其解,他非常郁闷。。亲爱的你能帮帮他吗?
问题是我们经常见到的整数划分,给出两个整数 n , m ,要求在 n 中加入m - 1 个乘号,将n分成m段,求出这m段的最大乘积
输入
第一行是一个整数T,表示有T组测试数据
接下来T行,每行有两个正整数 n,m ( 1<= n < 10^19, 0 < m <= n的位数);
输出
输出每组测试样例结果为一个整数占一行
样例输入
2
111 2
1111 2
样例输出
11
121
区间dp:整数划分
给出一个数,用乘号分成m块,求出分完之后值最大。
思路:dp[i][j] 表示从1到i的数中分成j块。
需要做一个预处理,求出a[i][j],表示i到j的数是多少。解决的方法就是从2到m进行自低向上的dp。
那么状态转移方程就是:dp[i][j] = max(dp[i][j],dp[k][j-1]*a[k+1][i])(k表示第j个括号可能插入的位置)。
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn = 100;
long long a[maxn][maxn],m;
long long dp[maxn][maxn];
int main()
{
freopen("in.txt","r",stdin);
int t;
scanf("%d",&t);
while(t--) {
char s[maxn];
scanf("%s%d",s+1,&m);
int len = strlen(s),flag = 1;
memset(a,0,sizeof(a));
for(int i = 1;i < len; i++) {
if(s[i] == '0')
flag = 0;
for(int j = i;j < len; j++) {
a[i][j] = a[i][j-1]*10 + s[j] - '0';
}
}
if(flag == 0 && len-1 == m || len -1 < m) {
printf("0\n");continue;
}
long long x ,ans ;
memset(dp,0,sizeof(dp));
for(int i = 0;i < len; i++) {
dp[i][1] = a[1][i];
}
ans = 0;
if(m == 1)
ans = dp[len-1][1];
for(int j = 2;j <= m; j++) {
for(int i = j;i < len; i++) {
ans = a[i][i];
for(int k = 1;k < i; k++) {
dp[i][j] = max(dp[i][j],dp[k][j-1]*a[k+1][i]);
}
}
}
printf("%lld\n",dp[len-1][m]);
}
return 0;
}