题目描述:
给出 N 个数字(0 ~ 9),在不改变他们顺序的情况下,加上 K 个乘号和 N - 1 - K 个加号,问最大值,其中 2 <= N <= 15, 0 <= K <= N - 1。
题解:
状态设计为 dp[i][k] 表示 1 ~ i 个数中插入 k 个乘号的最大值,对于一个区间,我们向其中插入一个乘号时(我们向每个元素前面插入乘号,即下标为 2 ~ N 前面都能插入乘号),我们要枚举乘号的位置才能知道插入在哪最优,所以对于一个区间,我们要知道插入位置前面的元素和与插入位置后面的元素和才能知道这个区间插入乘号后的值(因为括号可以任意加,所以一定是通过加括号让插入位置前后元素和进行相乘,这样得到的值最大),插入位置前面的元素和也就是dp[i - 1][k - 1],由状态设计我们知道,它是最优的,对于插入位置后面的元素和,是它们直接相加:sum[i] - sum[j - 1] (i 和 j - 1 含义具体看代码),原因如下:因为进行插入操作时插入位置后面的数还没有插入乘号,所以是它们的和。
在上述过程中,新的最优状态用到了之前最优的状态,同时之前最优的状态可以被多次利用,满足了动态规划的最优子结构和重叠子问题。所以由“最优大区间必须通过最优小区间才能推出”这一结论我们可以得出,最外层循环为区间的右端点(从2 到 N),因为在这个区间可能要插入多个乘号(同时要注意可能乘号不能全部插入,所以此时要满足乘号数量小于等于区间长度-1并且乘号数量要小于等于最大可插入的乘号数量),所以下一层循环为插入的这个区间乘号数,最内层循环也就变为枚举插入乘号的位置,so,状态转移方程为:
d
p
[
i
]
[
k
]
=
m
a
x
(
d
p
[
i
]
[
k
]
,
d
p
[
j
−
1
]
[
k
−
1
]
∗
(
s
u
m
[
i
]
−
s
u
m
[
j
−
1
]
)
,
j
<
i
dp[i][k]=max(dp[i][k],dp[j - 1][k - 1] * (sum[i] - sum[j-1]), j < i
dp[i][k]=max(dp[i][k],dp[j−1][k−1]∗(sum[i]−sum[j−1]),j<i
关于区间DP:
所谓区间动规,顾名思义,就是在一段区间上进行的动态规划。通常由一个二维数组dp[i][j]表示。一般i,j 的含义有以下几种。
- 表示从 i 个物品到第 j 个物品的最优值
- 表示从 i 开始,数据规模为 j 时的最优值
- 表示前 i 个物品,分成 j 段时的最优值
- …
AC Codes:
#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
//#include <unordered_set>
//#include <unordered_map>
#include <deque>
#include <list>
#include <iomanip>
#include <algorithm>
#include <fstream>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <cstdlib>
//#pragma GCC optimize(2)
using namespace std;
typedef long long ll;
//cout << fixed << setprecision(2);
//cout << setw(2);
const int N = 2e5 + 6, M = 1e9 + 7, INF = 0x3f3f3f3f;
long long dp[20][20], sum[20];
int main() {
//freopen("/Users/xumingfei/Desktop/ACM/test.txt", "r", stdin);
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int n, m;
cin >> n >> m;
int x;
for (int i = 1; i <= n; i++) {
cin >> x;
sum[i] += sum[i - 1] + x;
dp[i][0] = sum[i];
}
for (int i = 2; i <= n; i++) {
for (int k = 1; k <= i - 1 && k <= m; k++) {
for (int j = 2; j <= i; j++) {
dp[i][k] = max(dp[i][k], dp[j - 1][k - 1] * (sum[i] - sum[j - 1]));
}
}
}
cout << dp[n][m] << '\n';
return 0;
}