P1880 [NOI1995] 石子合并(环形区间dp模板)
题目描述:
在一个圆形操场的四周摆放 NN 堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出一个算法,计算出将 NN 堆石子合并成 11 堆的最小得分和最大得分。
输入格式
数据的第 11 行是正整数 NN,表示有 NN 堆石子。
第 22 行有 NN 个整数,第 ii 个整数 a_iai 表示第 ii 堆石子的个数。
输出格式
输出共 22 行,第 11 行为最小得分,第 22 行为最大得分。
输入输出样例
输入 #1
4 4 5 9 4
输出 #1
43 54
说明/提示
1\leq N\leq 1001≤N≤100,0\leq a_i\leq 200≤ai≤20。
思路:我最开始接触dp时看到最可怕的题,明明是入门题对当时的我来说却十分困难,不过经过了接近一年的从无到有的过程,
我也算是收获了许多,对于一些模板题也有了自己的理解,今天再次看到这道题,总算是再也不用抓耳挠腮,无从下手,最后还去
题解区看记忆化搜索的方法,真的特别感谢我的学长和其他支持我的人。
AC代码:
#include<bits/stdc++.h> using namespace std; int n; int a[205]; int dp[205][205][2]; int sum[205]; int main() { //freopen("test.txt", "r", stdin); scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); a[i + n] = a[i]; } if (n == 1) { printf("0\n"); printf("0\n"); return 0; } for (int i = 1; i <=2*n; i++) {//形成环则扩大为2n sum[i] = sum[i - 1] + a[i]; } for (int i = 1, j = i + 1; j <2*n; i++, j++) { dp[i][j][0] = dp[i][j][1] = sum[j] - sum[i - 1]; } for (int len = 3; len <= n; len++) { for (int i = 1, j = i + len - 1; j < 2*n; i++, j++) { dp[i][j][0] = 0x3f3f3f3f; for (int k = i; k < j; k++) {//从中间找分界点 dp[i][j][1] = max(dp[i][j][1], dp[i][k][1] + dp[k + 1][j][1] + sum[j] - sum[i - 1]); dp[i][j][0] = min(dp[i][j][0], dp[i][k][0] + dp[k + 1][j][0] + sum[j] - sum[i - 1]); } } } int ans1 = 0x3f3f3f3f, ans2 = 0; for (int i = 1; i <=n; i++) { ans1 = min(ans1, dp[i][i + n-1][0]); ans2 = max(ans2, dp[i][i + n-1][1]); } cout << ans1 << endl; cout << ans2 << endl; return 0; }