HDU 4283 You Are the One

区间DP。

我们先给人编号,从左到右编号1到n。

对于整段区间,必然有一个人是最后上场的,假设是编号为S的最后上场,

因为是栈维护的顺序,那么编号1至S-1这S-1个人,必然是第1--S-1个上场的(具体顺序不知),

而编号S+1至n这些人必然是第S--n-1个上场的(具体顺序不知)。

假设我们知道左边这些人上场的最优解与右边的人上场的最优解,

即可推导得出在编号S最后上场的情况下的最优解。

如何计算一段区间的最优解?可以设计区间DP,dp[i][j]表示编号i至j这群人上场的最优解。

假设现在要得到dp[st][en]这段区间最优解,

最优解为:min{ dp[st][s-1]+(en-st)*a[s]+dp[s+1][en]+left*(sum[en]-sum[s]) }

其中S是枚举最后哪个人上场,left表示st--S-1区间长度

上式中,dp[st][s-1]表示S左边编号st至s-1的人上场的最优解,(en-st)*a[s]表示S最后上场的代价,

dp[s+1][en]+left*(sum[en]-sum[s])表示S+1--en这一段最优解。

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

const int maxn=100+10;
int a[maxn],sum[maxn];
int dp[maxn][maxn];
int T,n;

void read()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
}

void work()
{
    for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) dp[i][j]=0x7FFFFFFF;
    memset(sum,0,sizeof sum);
    for(int i=1;i<=n;i++) sum[i]=sum[i-1]+a[i];

    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            int st=j,en=st+i-1; if(en>n) continue;
            if(i==1) {dp[st][en]=0;continue;}

            for(int s=st;s<=en;s++)
            {
                int left=s-st,right=en-s;
                if(!left) dp[st][en]=min(dp[st][en],
                                         dp[s+1][en]+a[s]*(i-1));

                else if(!right) dp[st][en]=min(dp[st][en],
                                               dp[st][s-1]+a[s]*(i-1));

                else dp[st][en]=min(dp[st][en],
                               dp[st][s-1]+
                               dp[s+1][en]+
                               (i-1)*a[s]+
                               left*(sum[en]-sum[s]));
            }
        }
    }
    printf("%d\n",dp[1][n]);
}

int main()
{
    int c=1;
    scanf("%d",&T);
    while(T--)
    {
        read();
        printf("Case #%d: ",c++);
        work();
    }
    return 0;
}

 

posted @ 2016-02-25 22:04  Fighting_Heart  阅读(382)  评论(0编辑  收藏  举报