P1880 [NOI1995]石子合并【区间DP】

时间:2021-11-17 09:14:48

题目描述

在一个圆形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。

试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.

输入输出格式

输入格式:

数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.

输出格式:

输出共2行,第1行为最小得分,第2行为最大得分.

输入输出样例

输入样例#1:

4
4 5 9 4

输出样例#1:

43
54
 #include<bits/stdc++.h>
using namespace std;
const int MAXN=;
const int INF=0x3f3f3f3f;
int dp[MAXN][MAXN],sum[MAXN],ans[MAXN],DP[MAXN][MAXN];
int jian(int i,int j){ return sum[j]-sum[i-]; }//i-1是因为前缀和要减去前一个而不是当前的那个。
int main()
{
int n;
scanf("%d",&n);
for (int i = ; i <=n ; ++i) {
scanf("%d",&ans[i]);
}
for (int i = ; i <=n+n ; ++i) {//拆开环,双向
ans[i+n]=ans[i];
sum[i]=ans[i]+sum[i-];//前缀和
}
memset(dp,, sizeof(dp));
for (int l = ; l <n ; ++l) {// 步长 ,l==1时,步长为二
for (int i = ,j=i+l; (j<n+n)&&(i<n+n) ; ++i,j=i+l) {
DP[i][j]=INF;
for (int k = i; k <j ; ++k) {//每一步当中的分割点
dp[i][j]=max(dp[i][j],dp[i][k]+dp[k+][j]+jian(i,j));//l-r的最大值
DP[i][j]=min(DP[i][j],DP[i][k]+DP[k+][j]+jian(i,j));
}
}
}
int MAX=,MIN=INF;
for(int i=;i<=n;i++)
{
MAX=max(MAX,dp[i][i+n-]);
MIN=min(MIN,DP[i][i+n-]);
}
printf("%d\n%d\n",MIN,MAX);
return ;
}