hdu4283(区间DP)

You Are the One

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1021    Accepted Submission(s): 484


Problem Description
  The TV shows such as You Are the One has been very popular. In order to meet the need of boys who are still single, TJUT hold the show itself. The show is hold in the Small hall, so it attract a lot of boys and girls. Now there are n boys enrolling in. At the beginning, the n boys stand in a row and go to the stage one by one. However, the director suddenly knows that very boy has a value of diaosi D, if the boy is k-th one go to the stage, the unhappiness of him will be (k-1)*D, because he has to wait for (k-1) people. Luckily, there is a dark room in the Small hall, so the director can put the boy into the dark room temporarily and let the boys behind his go to stage before him. For the dark room is very narrow, the boy who first get into dark room has to leave last. The director wants to change the order of boys by the dark room, so the summary of unhappiness will be least. Can you help him?
 

Input
  The first line contains a single integer T, the number of test cases.  For each case, the first line is n (0 < n <= 100)
  The next n line are n integer D1-Dn means the value of diaosi of boys (0 <= Di <= 100)
 

Output
  For each test case, output the least summary of unhappiness .
 

Sample Input
   
   
   
   
2    5 1 2 3 4 5 5 5 4 3 2 2
 

Sample Output
   
   
   
   
Case #1: 20 Case #2: 24
 

Source
2012 ACM/ICPC Asia Regional Tianjin Online
 

Recommend
liuyiding
 
本题题意为有一群人在排队,每个人都有一个不愉快值unhap[i],若该人第k个被服务,则他的不愉快度为k*unhap[i],可以通过一个堆栈改变顺序,是这群人总的不满度最小。
涉及最值问题,很容易想到贪心,但本题找不到一个贪心策略,因为人的顺序不能任意。不能贪心,有点失望了,但也坚定了动态规划解题的方向。我们回顾一下堆栈的性质,容易想到可以根据第一个人出来的次序将这群人分成三部分比该人先出来的,比该人后出来的,还有该人。比该人先出来的和比该人后出来的可以分别又用那种思想细分…直到某区间只有一个人为止。
于是动态规划的思想就出来了。可以江总的人数通过枚举中间点分成三部分分别求,然后合并取最小值。
设dp[i][j]为第i个人到第j个人的最小值,其中该区间的第一个人可以第1-j-i+1个被服务,假设第k个被服务,则该区间比他先服务的为dp[i+1][i+k-1],比他后服务的为dp[i+k][j]+unhap[i]*(k-1)+(sum[j]-sum[i+k-1])*k,其中sum[i]为从第一个人到底i个人的不愉快值的和。枚举k,取最小值,于是的状态转移方程:
                                 dp[i][j]=min(dp[i][j],dp[i+1][i+k-1]+dp[i+k][j]+unhap[i]*(k-1)+(sum[j]-sum[i+k-1])*k)
然后就好做了。
 
以下是转载某高人的区间DP的精辟见解,转载地址 http://www.cnblogs.com/zsboy/archive/2013/03/08/2950261.html在此表示感谢!

区间动态规划问题一般都是考虑,对于每段区间,他们的最优值都是由几段更小区间的最优值得到,是分治思想的一种应用,将一个区间问题不断划分为更小的区间直至一个元素组成的区间,枚举他们的组合 ,求合并后的最优值。
设F[i,j](1<=i<=j<=n)表示区间[i,j]内的数字相加的最小代价
最小区间F[i,i]=0(一个数字无法合并,∴代价为0)

每次用变量k(i<=k<=j-1)将区间分为[i,k]和[k+1,j]两段

For p:=1 to n do // p是区间长度,作为阶段。 
for i:=1 to n do // i是穷举的区间的起点
begin
j:=i+p-1; // j是 区间的终点,这样所有的区间就穷举完毕
if j>n then break; // 这个if很关键。
for k:= i to j-1 do // 状态转移,去推出 f[i,j]
f[i , j]= max{f[ i,k]+ f[k+1,j]+ w[i,j] } 
end; 
这个结构必须记好,这是区间动态规划的代码结构。

 

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

const int INF=100*100*100*10;
const int MAXN=100+10;
int unhap[MAXN];
int sum[MAXN];
int dp[MAXN][MAXN];

int Min(int a,int b)
{
	return a<b?a:b;
}

//////////////////////////////////递归写法//////////////////////////////
int DP(int s,int e)
{
	if(s>=e)
		return dp[s][e];
	if(dp[s][e]!=INF)
		return dp[s][e];
	for(int k=1;k<=e-s+1;k++)
	{
		dp[s][e]=Min(dp[s][e],unhap[s]*(k-1)+DP(s+1,s+k-1)+(sum[e]-sum[s+k-1])*k+DP(s+k,e));
	}
	return dp[s][e];
}


void Solve(int n)
{
	int len,s,k,e;
	memset(dp,0,sizeof(dp));
	for(s=1;s<=n;s++)
	{
		for(e=s+1;e<=n;e++)//e从s+1其是因为区间s-e存在且有意义;若s>e,这样的区间是不存在的,s=e表明不需等待故dp[s]e]=0.
		{
			dp[s][e]=INF;
		}
	}
	DP(1,n);
}

//////////////////////////////////非递归写法//////////////////////////////
void Solve(int n)
{
	int len,s,k,e;
	memset(dp,0,sizeof(dp));
	for(s=1;s<=n;s++)
	{
		for(e=s+1;e<=n;e++)//e从s+1其是因为区间s-e存在且有意义;若s>e,这样的区间是不存在的,s=e表明不需等待故dp[s]e]=0.
		{
			dp[s][e]=INF;
		}
	}

	for(len=1;len<=n;len++)//枚举区间长度
	{
		for(s=1;s<=n-len+1;s++)//枚举起始位置
		{
			e=s+len-1;
			for(k=1;k<=len;k++)//在该段区间内找
			{
				dp[s][e]=Min(dp[s][e],dp[s+1][s+k-1]+unhap[s]*(k-1)+(sum[e]-sum[s+k-1])*k+dp[s+k][e]);
			}
		}
	}
}


int main()
{
	int cas,i,n,tag=1;
	cin>>cas;
	while(cas--)
	{
		scanf("%d",&n);
		sum[0]=0;
		for(i=1;i<=n;i++)
		{
			scanf("%d",&unhap[i]);
			sum[i]=sum[i-1]+unhap[i];
		}
		Solve(n);	
		printf("Case #%d: %d\n",tag++,dp[1][n]);
	}
	return 0;
}

你可能感兴趣的:(动态规划)