【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\n%d", ans1, ans2); 29 return 0; 30 }