【动态规划】插入乘号问题

问题描述: 给出 N N N 个 1 1 1 - 9 9 9 的数字 ( v 1 , v 2 , … , v N ) (v1,v2,…,vN) (v1,v2,…,vN),不改变它们的相对位置,在中间加入 K K K 个乘号和 N − K − 1 N-K-1 N−K−1 个加号,括号随便加,使最终结果最大。因为乘号和加号一共就是 N − 1 N-1 N−1 个,所以恰好每两个相邻数字之间都有一个符号。 例如: N = 5 N=5 N=5, K = 2 K=2 K=2, 5 5 5 个数字分别为 1 1 1、 2 2 2、 3 3 3、 4 4 4、 5 5 5,可以进行如下运算: 1 ∗ 2 ∗ ( 3 + 4 + 5 ) = 24 1*2*(3+4+5)=24 1∗2∗(3+4+5)=24 1 ∗ ( 2 + 3 ) ∗ ( 4 + 5 ) = 45 1*(2+3)*(4+5)=45 1∗(2+3)∗(4+5)=45 ( 1 ∗ 2 + 3 ) ∗ ( 4 + 5 ) = 45 (1*2+3)*(4+5)=45 (1∗2+3)∗(4+5)=45 等等.

思路: 例如在计算 N = 5 N=5 N=5, K = 2 K=2 K=2 时,我们可以通过计算 m a x [ 0 ] [ 0 ] ∗ ( 2 + 3 + 4 + 5 ) max[0][0]*(2+3+4+5) max[0][0]∗(2+3+4+5)、 m a x [ 0 ] [ 1 ] ∗ ( 3 + 4 + 5 ) max[0][1]*(3+4+5) max[0][1]∗(3+4+5)、 m a x [ 0 ] [ 2 ] ∗ ( 4 + 5 ) max[0][2]*(4+5) max[0][2]∗(4+5)、 m a x [ 0 ] [ 3 ] ∗ ( 5 ) max[0][3]*(5) max[0][3]∗(5)等等这些式子的最大值得到最终的结果,这样就建立起了动态规划的思路。 状态转移方程已经体现在代码之中了。

源代码:

//
//  main.cpp
//  InsertMultiplier
//
//  Created by 胡昱 on 2022/1/5.
//

#include <iostream>
#include <cmath>
#include <cstring>
using namespace std;

// 数字数量及其最大值
int n;
const int MAX_N = 20;

// 乘号数量及其最大值
int k;
const int MAX_K = MAX_N - 1;

// 存储数字数组
int digits[MAX_N];

// 动态规划数组,dp[i][j]表示从第0个数字到第i个数字中插入j个乘号时所得到的最大值
long long dp[MAX_N][MAX_N];

// 累加数组,sum[i][j]代表了从第i个数字到第j个数字累加的结果
long long sum[MAX_N][MAX_N];


int main(int argc, const char * argv[]) {
          
   
    // 共M组测试用例
    int M;
    cin >> M;
    while((M--) > 0) {
          
   
        // 初始化(其实感觉不用初始化也可以)
        memset(digits, 0, sizeof(digits));
        memset(dp, 0, sizeof(dp));
        memset(sum, 0, sizeof(sum));
        
        // 输入数字数量n和乘号个数k
        cin >> n >> k;
        
        // 输入数字
        for(int ni = 0; ni < n; ++ni) {
          
   
            cin >> digits[ni];
        }
        
        // 初始化累加数组
        for(int ni = 0; ni < n; ++ni) {
          
   
            long long temp = 0;
            for(int nj = ni; nj < n; ++nj) {
          
   
                temp += digits[nj];
                sum[ni][nj] = temp;
            }
        }
        
        // 初始化动态规划数组(插入乘号数量为0时就等于一直累加)
        for(int ni = 0; ni < n; ++ni) {
          
   
            dp[ni][0] = sum[0][ni];
        }
        
        // 开始动态规划
        // 递推式挺好理解的,也就是取后面累加的值和前面某一段的最大值的乘积的最大值
        for(int ni = 0; ni < n; ++ni) {
          
   
            // 插入的乘号数量不仅要小于等于k,还要小于当前数字数量
            for(int ki = 1; ki <= k && ki <= ni; ++ki) {
          
   
                for(int nj = 0; nj < ni; ++nj) {
          
   
                    dp[ni][ki] = max(dp[nj][ki - 1] * sum[nj + 1][ni], dp[ni][ki]);
                }
            }
        }
        
        // 输出结果
        cout << dp[n - 1][k] << endl;
    }
    return 0;
}
经验分享 程序员 微信小程序 职场和发展