题目链接: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;
}

  

posted on 2019-06-07 22:30  月亮编程  阅读(218)  评论(0编辑  收藏  举报