【NOI1995】石子合并

本题在洛谷上的链接:https://www.luogu.org/problemnew/show/P1880


本来以为是一道简单的区间DP问题,草草地写了个程序结果样例都没过,仔细一看,原来n堆石子摆成了环。区间DP是线性DP的一种,写法比较固定,一般是先枚举区间长度,再枚举区间左端点,推出区间右端点,状态转移通常是枚举中间点。这道题虽然成了环,但本质是不变的,还是从2到n枚举区间长度,只不过区间左端点可以是1到n,区间右端点可以在左端点左侧。为了方便起见,我们可以直接把环变为链跑区间DP,仔细想想,可以把长度为n的环用长度为2*n-1的链来代替。只不过这样,就不能方便的把dp[1][n]作为答案了,而是当区间长度枚举到n时,用每次求出dp值更新答案。详见代码。

 1 #include <cstdio>
 2 #include <algorithm>
 3 
 4 using namespace std;
 5 
 6 const int maxn = 2 * 105, inf = 0x3f3f3f3f;
 7 
 8 int num[maxn], sum[maxn], dp1[maxn][maxn], dp2[maxn][maxn];
 9 
10 int main() {
11     int n, ans1 = inf, ans2 = 0;
12     scanf("%d", &n);
13     for (int i = 1; i <= n; ++i) scanf("%d", &num[i]);
14     for (int i = n + 1; i < 2 * n; ++i)
15         num[i] = num[i - n];
16     for (int i = 1; i < 2 * n; ++i)
17         sum[i] = sum[i - 1] + num[i];
18     for (int l = 2; l <= n; ++l)
19         for (int i = 1; i <= 2 * n - l; ++i) {
20             int j = i + l - 1;
21             dp1[i][j] = inf;
22             for (int k = i; k < j; ++k) {
23                 dp1[i][j] = min(dp1[i][j], dp1[i][k] + dp1[k + 1][j] + sum[j] - sum[i - 1]);
24                 dp2[i][j] = max(dp2[i][j], dp2[i][k] + dp2[k + 1][j] + sum[j] - sum[i - 1]);
25             }
26             if (l == n) ans1 = min(ans1, dp1[i][j]), ans2 = max(ans2, dp2[i][j]);
27         }
28     printf("%d
%d", ans1, ans2);
29     return 0;
30 }
AC代码
原文地址:https://www.cnblogs.com/Mr94Kevin/p/9602427.html