题目
题目描述
在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.
输入输出格式
输入格式:数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.
输出格式:输出共2行,第1行为最小得分,第2行为最大得分.
输入输出样例
4 4 5 9 4
43 54
分析
区间DP啦!
首先断环为链,把整个数组复制一遍。所以第一个要注意的就是数组大小。(必须都是两倍的MAXN,否则你就得到了Fast Runtime Error Transform的技能!)(我还RE了两次,貌似是题目的数据范围有更改但题面没有说明。)(你的好友AC率已下线)
然后开始DP乱搞写状态转移方程。区间DP的常规状态:dp[i][j]表示合并第i到第j个石子的最大/最小分数。状态转移方程就是:
dp[i][j] = min/max(dp[i][j], dp[i][k]+dp[i][k+1]+s[j]-s[i-1]);
(其中s是前缀和)
这个方程不难理解,其实就是i到k堆石子合并的分数加上k+1到j堆石子合并的分数再加上当前合并的分数(i到j的石子个数之和)。
但是不能像我一样愚蠢!
我一开始把转移方程写成了
dp[i][j] = min/max(dp[i+1][j]+s[j]-s[i-1], dp[i][j-1]+s[j][i-1]);
居然没想到合并一堆石子可以是由两堆已经进行过合并的石子产生的。(愚蠢!)
程序
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int MAXN = 500 + 10; 4 int n, stone[2*MAXN], mi[2*MAXN][2*MAXN], mx[2*MAXN][2*MAXN], s[2*MAXN]; 5 int main() 6 { 7 cin >> n; 8 for (int i = 1; i <= n; i++) 9 cin >> stone[i], stone[i+n] = stone[i]; 10 for (int i = 1; i <= 2*n; i++) 11 s[i] = s[i-1] + stone[i]; 12 for (int i = 2*n-1; i >= 1; i--) 13 { 14 for (int j = i+1; j < n+i; j++) 15 { 16 mi[i][j] = 0x3F3F3F3F; 17 for (int k = i; k < j; k++) 18 { 19 mi[i][j] = min(mi[i][j], mi[i][k]+mi[k+1][j]+s[j]-s[i-1]); 20 mx[i][j] = max(mx[i][j], mx[i][k]+mx[k+1][j]+s[j]-s[i-1]); 21 } 22 } 23 } 24 int ans1 = 0x3F3F3F3f, ans2 = 0; 25 for (int i = 1; i <= n; i++) 26 ans1 = min(ans1, mi[i][i+n-1]), ans2 = max(ans2,mx[i][i+n-1]); 27 cout << ans1 << endl << ans2 << endl; 28 return 0; 29 }