石子合并
先看一道比luogu p1880简单的题目。
有n堆石子排成一排,每堆石子有一定的数量,将n堆石子合并成一堆。合并的规则是每次只能合并相邻的两堆石子,合并的花费为这两堆石子的总数。石子经过n-1次合并后成为一堆,求总的最小花费和最大花费。
有多组测试数据,输入到文件结束。每组测试数据的第1行有一个整数n,表示有n堆石子,n<250。接下来的一行有n个数,分别表示这n堆石子的数目。每堆石子至少一颗,最多10000颗。
总的最小花费和最大花费。
3
2 4 5
17
20
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| #include<bits/stdc++.h> using namespace std;
const int INF=1<<30; const int N=300; int n,sum[N];
int Minval(){ int dp[N][N]; for(int i=1;i<=n;i++) dp[i][i]=0; for(int len=1;len<n;len++) for(int i=1;i<=n-len;i++) { int j=i+len; dp[i][j]=INF; for(int k=i;k<j;k++) dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]); } return dp[1][n]; } int Maxval(){ int dp[N][N]; for(int i=1;i<=n;i++) dp[i][i]=0; for(int len=1;len<n;len++) for(int i=1;i<=n-len;i++) { int j=i+len; dp[i][j]=-INF; for(int k=i;k<j;k++) dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]); } return dp[1][n]; } int main() { scanf("%d",&n); sum[0]=0; for(int i=1;i<=n;i++) { int x; scanf("%d",&x); sum[i]=sum[i-1]+x; } printf("%d\n%d",Minval(),Maxval()); return 0; }
|
环形石子合并
接下来就可以着手解决luogu p1880了。如果按照上面的解法解这道题,会出现“min值永远比正确答案大1”的“奇怪”现象,这是因为没有考虑环形的缘故。
在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。
试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.
数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.
输出共2行,第1行为最小得分,第2行为最大得分.
4
4 5 9 4
43
54
解题思路:
- 区间DP
- 处理环形情况的通用套路:在任意位置把环断开成链,复制一倍接在末尾
参考代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| #include<bits/stdc++.h> using namespace std;
const int INF=1<<30; const int N=240; int n,sum[N],x,arr[N];
int Minval(){ int dp[N][N],ans=INF; for(int i=1;i<=n;i++) dp[i][i]=0; for(int len=1;len<n;len++) for(int i=1;i<=2*n-len;i++) { int j=i+len; dp[i][j]=INF; for(int k=i;k<j;k++) dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]); } for(int i=1;i<=n;i++) { ans=ans>dp[i][i+n-1]?dp[i][i+n-1]:ans; } return ans; } int Maxval(){ int dp[N][N],ans=-INF; for(int i=1;i<=n;i++) dp[i][i]=0; for(int len=1;len<n;len++) for(int i=1;i<=2*n-len;i++) { int j=i+len; dp[i][j]=-INF; for(int k=i;k<j;k++) dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]); } for(int i=1;i<=n;i++) { ans=ans<dp[i][i+n-1]?dp[i][i+n-1]:ans; } return ans; } int main() { scanf("%d",&n); sum[0]=0; for(int i=1;i<=n;i++) { scanf("%d",&arr[i]); arr[i+n]=arr[i]; } for(int i=1;i<=2*n;i++) { sum[i]=sum[i-1]+arr[i]; } printf("%d\n%d",Minval(),Maxval()); return 0; }
|
hdu 3506 “Monkey Party”