题目链接
环形石子合并。我们需要做的是尽量向简单的问题转化,可以把前n-1堆石子一个个移到第n个后面,那样环就变成了线,即现在有2*n-1堆石子需要合并,我们只要求下面的式子即可。求法与上面那题完全一样。
min(dp[s][s+n-1])s∈[1,n]
在这里采用四边形优化
用s[i][j]表示区间[i,j]中的最优分割点,那么第三重循环可以从[i,j-1)优化到【s[i][j-1],s[i+1][j]】。(这个时候小区间s[i][j-1]和s[i+1][j]的值已经求出来了,然后通过这个循环又可以得到s[i][j]的值)。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define INF 0x3f3f3f3f
using namespace std;
const int N=2010;
int a[N];//存数
int sum[N];//维护前缀和
int dp[N][N];//dp[i][j]表示合并第i堆到第j堆石子的最小代价
int s[N][N];//s[i][j]表示区间[i,j]的最优分割点
int main()
{
//freopen("in.txt","r",stdin);
int ans,n,i,j,len,k;
while(~scanf("%d",&n))
{
sum[0]=0;
memset(dp,0x3f,sizeof(dp));
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum[i]=sum[i-1]+a[i];
s[i][i]=i;
dp[i][i]=0;
}
//把前n-1堆石子移到第n堆后面,环变成线
for(i=1;i<n;i++)
{
sum[i+n]=sum[i+n-1]+a[i];
s[i+n][i+n]=i+n;
dp[i+n][i+n]=0;
}
for(len=2;len<=n;len++)
for(i=1;i<=2*n-1;i++)
{
j=i+len-1;
if(j>2*n-1)break;
for(k=s[i][j-1];k<=s[i+1][j];k++)
//从[i,j-1)优化到[s[i][j-1],s[i+1][j]]
//四边形优化
if(dp[i][j]>dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1])
{
dp[i][j]=dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1];
s[i][j]=k;//存最优分割点
}
}
ans=INF;
for(i=1;i<=n;i++)
ans=min(ans,dp[i][i+n-1]);
printf("%d\n",ans);
}
return 0;
}