HDU 4283 You Are the One(区间DP)

来源:互联网 发布:赛尔网络个人业务 编辑:程序博客网 时间:2024/06/16 22:05

题目:You Are the One

    题意: 
    n个男孩排成队登台,每个男孩有一个屌丝值Di 
    若某男孩第k个登台 ,他将产生Di*(k-1)的愤怒值 
    要求所有男孩愤怒值最小 
    可以做的操作是将男孩丢进栈里面改变登台顺序(栈满足先进后出 )  
    分析: 
    dp[i][j]表示区间[i,j]所有男孩的最小愤怒值 
    if (i == j) return 0;//第一个登台愤怒值为0 
    状态转移: 
    首先明确的是区间[i,j]内共有j-i+1个人 (只考虑这j-i+1个人) 
    当从区间[i+1,j]到 区间[i,j],加入了第i个人 
    考虑第i个人第几个登台,假设他第k个登台 
    原本在[i,j]区间里面,他应该第一个登台 
    现在第k个登台,表示他后面k-1个人先于他登台(因为栈的特性) 
    所以现在顺序是[i+1,(i+1)+(k-1)-1]登台,i第k个登台, 
    后面的[(i+1)+(k-1),j]再登台 
    但是状态转移并不是dp[i+1][(i+1)+(k-1)-1]+a[i]*(k-1)+dp[(i+1)+(k-1)][j] 
    因为dp[(i+1)+(k-1)][j]是指这些人按从1开始的顺序登台 
    而实际上后面区间[(i+1)+(k-1),j]的登台是从k+1到(j-i+1)的顺序 
    其实只需再加上 k*∑a[l] ((i+1)+(k-1) <= l <= j) 
    也就是说,区间 [(i+1)+(k-1),j]里面每个人的登台都往后推了k位 
    所以最后的状态转移方程是:dp[i][j] = min 
    dp[i+1][(i+1)+(k-1)-1]+a[i]*(k-1)+dp[(i+1)+(k-1)][j] + k * sum[(i+1)+(k-1)][j] 
    其中sum[l][r]表示区间[l,r]的人的屌丝值之和


#define mem(a,x) memset(a,x,sizeof(a))#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<queue>#include<set>#include<stack>#include<cmath>#include<map>#include<stdlib.h>#include<cctype>#include<string>#define Sint(n) scanf("%d",&n)#define Sll(n) scanf("%I64d",&n)#define Schar(n) scanf("%c",&n)#define Sint2(x,y) scanf("%d %d",&x,&y)#define Sll2(x,y) scanf("%I64d %I64d",&x,&y)#define Pint(x) printf("%d",x)#define Pllc(x,c) printf("%I64d%c",x,c)#define Pintc(x,c) printf("%d%c",x,c)using namespace std;typedef long long ll;/*题意:n个男孩排成队登台,每个男孩有一个屌丝值Di若某男孩第k个登台 ,他将产生Di*(k-1)的愤怒值要求所有男孩愤怒值最小可以做的操作是将男孩丢进栈里面改变登台顺序(栈满足先进后出 ) 分析:dp[i][j]表示区间[i,j]所有男孩的最小愤怒值if (i == j) return 0;//第一个登台愤怒值为0状态转移:首先明确的是区间[i,j]内共有j-i+1个人 (只考虑这j-i+1个人)当从区间[i+1,j]到 区间[i,j],加入了第i个人考虑第i个人第几个登台,假设他第k个登台原本在[i,j]区间里面,他应该第一个登台现在第k个登台,表示他后面k-1个人先于他登台(因为栈的特性)所以现在顺序是[i+1,(i+1)+(k-1)-1]登台,i第k个登台,后面的[(i+1)+(k-1),j]再登台但是状态转移并不是dp[i+1][(i+1)+(k-1)-1]+a[i]*(k-1)+dp[(i+1)+(k-1)][j]因为dp[(i+1)+(k-1)][j]是指这些人按从1开始的顺序登台而实际上后面区间[(i+1)+(k-1),j]的登台是从k+1到(j-i+1)的顺序其实只需再加上 k*∑a[l] ((i+1)+(k-1) <= l <= j)也就是说,区间 [(i+1)+(k-1),j]里面每个人的登台都往后推了k位所以最后的状态转移方程是:dp[i][j] = mindp[i+1][(i+1)+(k-1)-1]+a[i]*(k-1)+dp[(i+1)+(k-1)][j] + k * sum[(i+1)+(k-1)][j]其中sum[l][r]表示区间[l,r]的人的屌丝值之和 */const int N = 111;int dp[N][N];int sum[N];//其实一维数组也可以就不用二维数组了sum[r] - sum[l-1]代替sum[l][r]int a[N];int DP(int i,int j){if (i >= j) return 0;if (~dp[i][j]) return dp[i][j];//枚举第i个人的登台顺序dp[i][j] = DP(i+1,j) + a[i]*((j-i+1)-1);//最后一个登台 for (int k = 1;k < j-i+1;++k){int mid = (i+1)+(k-1)-1;dp[i][j] = min(dp[i][j],DP(i+1,mid)+a[i]*(k-1)+DP(mid+1,j)+k*(sum[j]-sum[mid])) ;} return dp[i][j];}int main(){    int T;Sint(T);int kas = 0;    while (T--)    {    int n;Sint(n);    for (int i = 1;i <= n;++i) {Sint(a[i]);sum[i] = sum[i-1]+a[i];}mem(dp,-1); printf("Case #%d: %d\n",++kas,DP(1,n));}    return 0;}


0 0
原创粉丝点击