题目
将 n 堆石子绕圆形操场排放,现要将石子有序地合并成一堆。
规定每次只能选相邻的两堆合并成新的一堆,并将新的一堆的石子数记做该次合并的得分。
请编写一个程序,读入堆数 n 及每堆的石子数,并进行如下计算:
- 选择一种合并石子的方案,使得做 n-1 次合并得分总和最大。
- 选择一种合并石子的方案,使得做 n-1 次合并得分总和最小。
输入格式
第一行包含整数 n,表示共有 n 堆石子。
第二行包含 n 个整数,分别表示每堆石子的数量。
输出格式
输出共两行:
第一行为合并得分总和最小值,
第二行为合并得分总和最大值。
数据范围
1 < n < 200
输入样例:
4
4 5 9 4
输出样例:
43
54
区间dp的基本模板
迭代:
for(int lena=1;lena<=n;lena++){
for(int l=1;l + lena -1 <=n;l++){
r = l + lena - 1;
}
}
环形问题的处理方法 ,把这个len = n 的环 换成一个2*n的链,比如,如果把1 2 3 4 5这个环改成1 2 3 4 5 -1 2 3 4 5这个链, 对于i在[1,n] , dp[i][i+n-1]刚好能取到环的分割的每一种情况.那就把环形石子合并转换成 直线的石子合并.
int n;
int a[MAXN];
int g[MAXN][MAXN],f[MAXN][MAXN]; //最大和最小
int sum[MAXN];
int main(){
scanf("%d",&n);
for(int i =1;i<=n;i++){
scanf("%d",&a[i]);
a[n+i] = a[i]; //构造
}
for(int i=1;i<=2*n;i++){ //前缀和
sum[i] =sum[i-1] + a[i];
}
memset(g,0x3f,sizeof(g));
memset(f,-0x3f,sizeof(f));
for(int lena=1;lena<=n;lena++){
for(int l=1;l+lena-1 <=n*2;l++){
int r = l + lena -1;
if(l == r){
g[l][r] = f[l][r] = 0;
}
else {
for(int k = l;k < r;k++){
g[l][r] = min(g[l][r],g[l][k]+g[k+1][r] + (sum[r] - sum[l-1]));
f[l][r] = max(f[l][r],f[l][k]+f[k+1][r] + (sum[r] - sum[l-1]));
}
}
}
}
int maxn = -INF,minx = INF;
for(int i=1;i<=n;i++){ //看n种分割哪种最大和最小
minx = min(minx,g[i][i+n-1]);
maxn = max(maxn,f[i][i+n-1]);
}
printf("%d\n%d\n",minx,maxn);
return 0;
}