【动态规划】插入乘号问题
问题描述: 给出 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;
}
