NYOJ746 整數劃分 題解 區間DP

題目連接: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。
據此,咱們能夠編寫代碼以下:ios

#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;
}
相關文章
相關標籤/搜索