HDU 4283 You Are the One(区间DP)

题目: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] = 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]的人的屌丝值之和 
*/
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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值