HDU-4238-You Are the One (区间DP)

来源:互联网 发布:linux 关闭系统 编辑:程序博客网 时间:2024/05/16 17:10

传送门:http://acm.hdu.edu.cn/showproblem.php?pid=4283

You Are the One

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


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  512345554322
 

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

有一个队列要上舞台,有一个栈可以进行对队列顺序的调度,在队列中每个人有一个愤怒比率,该人的愤怒值等于该人的愤怒比率*(k-1)(k代表该人第k个上场的)。问怎样调度能够使得 总 的愤怒值最小。

题解:

该题一开始就是想得用贪心做,题目也没说每个人进栈出栈的次数,所以感觉没问题,可是看了题解后竟然是区间Dp,理解了也是很长时间。

说一下我对这个区间DP解法的理解:对于队列 (L  (i , j) ,(k  R)),对于任意区间[L,R]进行调度,假设这个区间是在队列的最前端,那么对L可以放到[L,r]的任何一个位置,假设L是第K个出场的,那么区间[i,j]的人要先与L入场,区间[k,R]的人要后于L入场,此时L的愤怒值为d[L]*(k-1);区间[i,j]和区间[k,R]的操作也是一样的(注意对每个区间操作时先都把它当做是队列的最前段的区间)。那么怎么计算区间的愤怒值呢?对于先于L入场的区间[i,j]而言,因为在计算区间[i,j]的时候本身就是把区间[i,j]当做队列的最前段来计算的,而在计算区间[L,R],区间[i,j]仍然在最前段没有改变入场的位置所以其愤怒值就是它本身。(**)而对于区间[k,R],在计算它的时候也是先把它当做队列的最前段来计算的,但是现在在队列[L,R]中变成了在k个人的后面了,也就是其愤怒值增加了,那么增加了多少呢?记sum[i]表示前i个人的愤怒比率和,那么区间[k,R]的愤怒值 增加了 k*(sum[R]-sum[k-1]).至此可以写一下状态转移方程的通式了:

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

*笔者不才,写的逻辑有点混乱,但是多读几遍就能知道它的整个过程了,再结合代码看看。

#include <iostream>#include <cstring>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cmath>#define INF 0x3f3f3f3f;using namespace std;int d[111],dp[111][111],sum[111];int main(){    int T,w=0;    cin>>T;    while(T--)    {        int n;        scanf("%d",&n);        memset(dp,0,sizeof(dp));        sum[0]=0;        for(int i=1;i<=n;i++)        {            scanf("%d",&d[i]);            sum[i]=sum[i-1]+d[i];        }        for(int len=1;len<n;len++)        {            for(int i=1;i+len<=n;i++)            {                int j=i+len;                dp[i][j]=INF;///只设置从i->j是INF,因为可能出现区间(l,r)使得r<l的情况(当k=1||k==len+1时),此时dp==0                for(int k=1;k<=len+1;k++)                {                    dp[i][j]=min(dp[i][j],dp[i+1][i+k-1]+dp[i+k][j]+d[i]*(k-1)+k*(sum[j]-sum[i+k-1]));                }            }        }        printf("Case #%d: %d\n",++w,dp[1][n]);    }    return 0;}


原创粉丝点击