hdu 4283 You Are the One

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4283

题意是现在一场表演,有n个人排成一队要上台表演,由每个人有一个权值D[i],第i个人上场时总的答案要加上(i-1)*D[i];

然后现在有一个栈供我们进行调整出场顺序,问如何调整使得答案最小,输出这个答案;


我们分析一下可以看出来,如果可以随便调整的话,那么肯定就是把值从大到小放置,但是栈的话有一些情况是没办法把顺序

调整到刚好从大到小的。一开始想了一个dp[i][j]代表第i个数的时候栈里有j个数字的最小答案,但是发现好像不方便转移,因为要记一

下当时栈里j个数是哪些数字。后来发现栈的本质就是把顺序做出一些调整那应该就是一个区间DP了,然后研究一下栈的性质,发现

假设初始序列是1 2 3 4 5 6 ……那么经过栈调整之后的序列,对于每一个数字,假设该数字后面没有比他大的数字的话,那么从

这个数字往后的序列必须是逆序的,比如1 2 3 4 经过调整后1 4 2 3 这种情况是不可能出现的,必须是1 4 3 2

有了这些性质之后我们就可以进行转移了,记dp[i][j]为从i到j的调整后的最小答案,这么进行转移时枚举k,我们把k到j这个区间逆序后,插到

i到k - 1后面(i到k-1无论顺序变成什么样这样转移都是符合栈规律的);还有就是把i到k-1逆序后插到 k到 j后面(同样也是符合栈规律的);

还有一种情况就是k到j这个区间放在i到k-1这个区间后面,(这个无论两个区间怎么转移都是合法的,其实这个才是最关键的方程);

那么第三个情况的方程就是                        

dp[i][j] = min (dp[i][j] ,dp[i][k] + dp[k+1][j] + (sum[j] - sum[k]) *(k - i + 1) );

其实后来我发现第三个方程已经囊括了前面两种情况啊!!!!这个东西我一开始就想到了,但是被我否认了,于是用了两三个小时想出

面的两个方程,再由前面两个再验证了这个方程,真的好气啊!!!!

#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<map>
#include<time.h>
#include<cstdio>
#include<vector>
#include<stack>
#include<queue>
#include<iostream>
using namespace std;
#define  LONG long long
const int   INF=0x3f3f3f3f;
const LONG    MOD=1e9+7;
const double PI=acos(-1.0);
#define clr0(x) memset(x,0,sizeof x)
#define clrI(x) memset(x,-1,sizeof(x))
#define clr1(x) memset(x,INF,sizeof x)
#define clr2(x) memset(x,-INF,sizeof x)
#define EPS 1e-10
LONG dp[150][150];
LONG D[150];
LONG sum[150];
int main()
{
//    freopen("C:\\Users\\ZhangYuyang\\Desktop\\in.txt","r",stdin);
//freopen("C:\\Users\\ZhangYuyang\\Desktop\\out.txt","w",stdout);
    int T;
    int ca = 1;
    cin>>T;
    while(T--)
    {
        int n ;
        cin>>n;
        sum[0 ] = 0;
        for(int i = 1 ;i <= n ; ++ i)
            scanf("%lld",&D[i]) ,
            sum[i] = D[i] + sum[i - 1];
        clr1(dp);
        for(int i = 1; i<= n ;++ i)
            dp[i][i] = 0 ;
        for( int l = 1; l <= n ;++ l)
        {
            for(int i = 1; i < n ;++ i)
            {
                int j = i + l ;
                if(j > n) break ;
                for(int k = i; k <= j ;++ k)
                {
                    if(k == j)
                        dp[i][j] = min(dp[i][j] , dp[i][j-1] + D[j] * l);
                    if(k < j)
                    {
                        LONG tmp = 0;
                        LONG temp = l ;
                        for(int t = i ; t <= k ;++ t)
                            tmp += D[t] * (temp -- );
                        dp[i][j] = min(dp[i][j] , dp[k + 1][j] + tmp);
                        dp[i][j] = min (dp[i][j] ,dp[i][k] + dp[k+1][j] + (sum[j] - sum[k]) *(LONG )(k - i + 1) );
                    }
                }
             }
        }
        printf("Case #%d: ",ca++);
        cout<<dp[1][n]<<endl;
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值