【题解】hdu3506 区间DP

题目链接
环形石子合并。我们需要做的是尽量向简单的问题转化,可以把前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;
}

猜你喜欢

转载自blog.csdn.net/qq_41958841/article/details/81636516