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

问题描述:
给出 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 NK1 个加号,括号随便加,使最终结果最大。因为乘号和加号一共就是 N − 1 N-1 N1 个,所以恰好每两个相邻数字之间都有一个符号。
例如: 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 12(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 (12+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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值