前往:我自己搭建的博客
题目
题解
这是一道经典的区间dp题,f[l][r]表示将区间[l,r]之间的石子合并后的最值。可以由长度小的区间推得长度大的区间的信息。题中数据是环状的,需要断环为链,将原序列复制并加到后面。
代码
#include <bits/stdc++.h>
using namespace std;
const int inf=0x7fffffff;
const int maxn=200+5;
int n;
int f1[maxn][maxn],f2[maxn][maxn],a[maxn],sum[maxn];
//sum[]是前缀和,f1,f2[][]分别表示最小/大值
inline void dp()
{
for(int i=1;i<=(n<<1);i++)for(int j=i+1;j<=(n<<1);j++) f1[i][j]=inf,f2[i][j]=-inf;
for(int len=2;len<=n;len++) //区间长度
{
for(int l=1; ;l++) //区间左端点
{
int r=l+len-1; if(r>(n<<1)) break;
for(int k=l;k<r;k++) //枚举断点
{
f1[l][r]=min(f1[l][r],f1[l][k]+f1[k+1][r]+sum[r]-sum[l-1]);
f2[l][r]=max(f2[l][r],f2[l][k]+f2[k+1][r]+sum[r]-sum[l-1]);
}
}
}
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) {scanf("%d",&a[i]); a[i+n]=a[i];}
for(int i=1;i<=(n<<1);i++) sum[i]=sum[i-1]+a[i];
dp(); int ans_min=inf,ans_max=-inf;
for(int l=1; ;l++)
{
if(l>n+1) break;
ans_min=min(ans_min,f1[l][l+n-1]);
ans_max=max(ans_max,f2[l][l+n-1]);
}
printf("%d\n%d\n",ans_min,ans_max);
return 0;
}