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");
}
原文地址:https://www.cnblogs.com/fxh0707/p/14142453.html