NC50493 环形石子合并
状态表示:\(f(i,j)\):从下标\(i\)合并到下标\(j\)的最大价值。
先看石子合并(\(n\)堆石子):
\[1,2,...,n
\]
\(f(1,n)\)即为答案。
再看环形:
最后的答案为:\(f(1,n)(f(n,1)和f(1,n)结果相同),f(2,1),f(3,2),...,\)中的最小值。
我们当然不能在环上直接\(DP\),因为这样\(f(2,1),f(3,2)\)的合并结果不一定构成环形,可能是直接将相邻的两堆合并来得到\(f(2,1),f(2,3)\)的值。
于是,破环成链应运而生!
将石子\(1~n\)复制一遍,得到:
\[1,2,...,n,1,2,...,n
\]
参考石子合并,我们可以得到\(f(1,n),f(2,1),...,f(n,n-1)\)的值,最后在它们中取最优值即可。
const int N=410;//两倍空间
int f[N][N];//min
int g[N][N];//max
int a[N<<1];
int sum[N<<1];
int n;
int main()
{
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i],a[i+n]=a[i];
for(int i=1;i<=n*2;i++) sum[i]=sum[i-1]+a[i];
memset(f,0x3f,sizeof f);
for(int i=n*2;i>=1;i--)
for(int j=i;j<=n*2;j++)
{
if(i == j) f[i][j]=g[i][j]=0;
else
for(int k=i;k<j;k++)
{
f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]+sum[j]-sum[i-1]);
g[i][j]=max(g[i][j],g[i][k]+g[k+1][j]+sum[j]-sum[i-1]);
}
}
int resf=INF,resg=0;
for(int i=1;i<=n;i++)
{
resf=min(resf,f[i][i+n-1]);
resg=max(resg,g[i][i+n-1]);
}
cout<<resf<<endl;
cout<<resg<<endl;
//system("pause");
}