题目链接:http://nyoj.top/problem/746
【题目描述】
《746-整数划分(四)》
暑假来了,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)。
首先我们来看,这道题目怎么建立起它的状态转移方程呢?
我们假设输入的数字 a 一共有 n 为,从高位到低位分别标记为第1位,第2位,……,第n为。
我们用 dp[i][j] 表示 a 的前 i 位使用了 j 个乘号所得到的最大的结果,用 num[L][R] 表示 a 从第L位到第R位所组成的数 那么:
当j==0时:
dp[i][j]=num[1][i]
当j>0时:
dp[i][j] = max(dp[k][j-1] + num[k+1][i]), j≤k<i。
据此,我们可以编写代码如下:
#include <iostream> #include <algorithm> using namespace std; const int maxn = 22; int n, m, T; long long a; long long ten_pow[20]; // ten_pow[i] 表示 10 的 i 次方 long long dp[maxn][maxn]; void init_ten_pow() { // 用于初始化ten_pow[] ten_pow[0] = 1; for (int i = 1; i <= 19; i ++) ten_pow[i] = ten_pow[i-1] * 10LL; } void get_digits() { // 用于获得a的位数的函数 n = 1; long long tmp = 10LL; while (a / tmp) { tmp *= 10LL; n ++; } } long long get_num(int L, int R) { // 这个函数用于求数组a从左往右数第L位到第R位这个区间范围对应的数 return a / ten_pow[n-R] % ten_pow[R-L+1]; } int main() { cin >> T; init_ten_pow(); while (T --) { fill(dp[0], dp[0]+maxn*maxn, 0); cin >> a >> m; get_digits(); for (int i = 1; i <= n; i ++) { // i 表示数 a 从左往右数第i位,从1开始 dp[i][0] = get_num(1, i); for (int j = 1; j < m && j < i; j ++) { // j 表示 第 1 至 i 位,并且以第i位结尾的,使用了j次乘号能得到的最大值 for (int k = j; k < i; k ++) { dp[i][j] = max(dp[i][j], dp[k][j-1] * get_num(k+1, i)); } } } cout << dp[n][m-1] << endl; } return 0; }