HDU-3506 Monkey Party (环形石子合并)

题目大意:n堆石子围成一圈,每堆石子的块数已知,每次可以将相邻的两堆合并到一堆,块数变为两堆之和,代价也为两堆石子块数之和。求合并到一堆的最小代价。

题目分析:先通过将前n-1依次个移到第n个后面,将环变成线。定义状态dp(i,j)表示将区间(i,j)的石子合并所需的最小代价,则状态转移方程为dp(i,j)=min(dp(i,k)+dp(k+1,j)+sum(i,j))。时间复杂度为O(n*n*n),利用四边形不等式优化,限制k(i,j)的取值范围在k(i,j-1)~k(i+1,j)之间,达到优化效果。

 

代码如下:

# include<iostream>
# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;

const int INF=1<<30;
const int N=1005;

int n;
int dp[N<<1][N<<1];
int s[N<<1][N<<1];
int sum[N<<1];
int a[N<<1];

void init()
{
    sum[0]=0;
    for(int i=1;i<=n;++i){
        scanf("%d",a+i);
        sum[i]=a[i]+sum[i-1];
    }
    for(int i=1;i<n;++i){
        a[n+i]=a[i];
        sum[n+i]=a[n+i]+sum[n+i-1];
    }
}

void solve()
{
    for(int l=1;l<=n;++l){
        for(int i=0;i+l-1<2*n;++i){
            int j=i+l-1;
            if(l==1){
                dp[i][i]=0;
                s[i][i]=i;
            }else{
                dp[i][j]=INF;
                for(int k=s[i][j-1];k<=s[i+1][j];++k){
                    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;
                    }
                }
            }
        }
    }
    int ans=INF;
    for(int i=1;i<=n;++i)
        ans=min(ans,dp[i][n+i-1]);
    printf("%d\n",ans);
}

int main()
{
    while(~scanf("%d",&n))
    {
        init();
        solve();
    }
    return 0;
}

  

posted @ 2016-03-19 16:31  20143605  阅读(1624)  评论(0编辑  收藏  举报