HDU4283 You Are the One —— 区间DP

题目链接:https://vjudge.net/problem/HDU-4283

 

You Are the One

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


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
 

 

Recommend
liuyiding

 

 

题意:

有n个人站成一排准备上台表演,由于每个人都想是第一个站到台上,所以如果需要等待就会不开心。每个人都有一个值a[i],表示每等待一个人,不开心度就会+a[i]。工作人员为了使得所有人的不开心总值最小,就设置了一个小黑屋(一个栈),可以把队列的人先安排到小黑屋里,以此调节出场顺序。

 

题解:

1.可知:在一个区间 [l, r] 内, 如果l是第 k (1<=k<=r-l+1)个出场的,那么 [l+1, l+k-1] 必定是先与l出场的(根据栈的性质,如果要得到栈底元素,那么必须把栈底元素上面的所有元素出栈), 所以剩下的 [l+k, r] 是在l之后出场的。

2.根据第一条结论,如果确定了l在区间 [l, r] 是第k个出场的(不需要考虑区间之外的人),那么就可以把区间分为 [l+1, l+k-1] 和 [l+k, r],且这两个区间是互不影响的,所以又可以根据上述结论分别对这两个区间单独求值。

3.那怎么计算区间的不开心总值呢?

由于我们知道l在当前区间内是第k个出场的,所以我们可以先得到 a[l]*(k-1)。对于区间[l+1, l+k-1]的人,由于他们是先于l出场的,所以对于这个总体来说,他们是没有延迟的,所以直接加上dp[l+1, l+k-1]。对于区间[l+k, r]的人,由于他们是在l之后出场的,而l在这个区间内又是第k个出场的,所以对于 [l+k, r]这个总体来说,他们是整体延迟了k个人的,所以先加上 k*(sum[r]-sum[l+k-1]),加上了延迟所带来的不开心值之后,我们就可以把 区间[l+k, r]的出场是没有延迟的,所以情况跟区间[l+1, l+k-1]一样,再加上dp[l+k, r]就行了。

 

学习之处

1.栈的性质: 如果要得到栈底元素,那么必须把栈底元素上面的所有元素出栈(这个结论虽然显而易见,但是由这个结论再推出另外的结论却并非易事)。

2.求值除了一步求得之外,还可以分步求值,一个阶段只求一部分。

 

 

记忆化搜索:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 #include <vector>
 6 #include <cmath>
 7 #include <queue>
 8 #include <stack>
 9 #include <map>
10 #include <string>
11 #include <set>
12 using namespace std;
13 typedef long long LL;
14 const int INF = 2e9;
15 const LL LNF = 9e18;
16 const int MOD = 1e9+7;
17 const int MAXN = 100+10;
18 
19 int a[MAXN];
20 int sum[MAXN], dp[MAXN][MAXN];
21 
22 int dfs(int l, int r)
23 {
24     if(l>=r) return 0;
25     if(dp[l][r]!=-1) return dp[l][r];
26 
27     dp[l][r] = INF;
28     for(int k = 1; k<=r-l+1; k++)
29     {
30         int tmp = dfs(l+1, l+k-1)+dfs(l+k,r)+(k-1)*a[l]+k*(sum[r]-sum[l+k-1]);
31         dp[l][r] = min( dp[l][r], tmp );
32     }
33     return dp[l][r];
34 }
35 
36 int main()
37 {
38     int T, n, kase = 0;
39     scanf("%d", &T);
40     while(T--)
41     {
42         scanf("%d", &n);
43         sum[0] = 0;
44         for(int i = 1; i<=n; i++)
45             scanf("%d", &a[i]), sum[i] = sum[i-1]+a[i];
46 
47         memset(dp, -1, sizeof(dp));
48         dfs(1, n);
49         printf("Case #%d: %d\n", ++kase, dp[1][n]);
50     }
51 }
View Code

 

递推:

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 #include <vector>
 6 #include <cmath>
 7 #include <queue>
 8 #include <stack>
 9 #include <map>
10 #include <string>
11 #include <set>
12 using namespace std;
13 typedef long long LL;
14 const int INF = 2e9;
15 const LL LNF = 9e18;
16 const int MOD = 1e9+7;
17 const int MAXN = 100+10;
18 
19 int a[MAXN];
20 int sum[MAXN], dp[MAXN][MAXN];
21 
22 int main()
23 {
24     int T, n, kase = 0;
25     scanf("%d", &T);
26     while(T--)
27     {
28         scanf("%d", &n);
29         sum[0] = 0;
30         for(int i = 1; i<=n; i++)
31             scanf("%d", &a[i]), sum[i] = sum[i-1]+a[i];
32 
33         memset(dp, 0, sizeof(dp));
34         for(int len = 2; len<=n; len++)
35         {
36             for(int l = 1; l<=n-len+1; l++)
37             {
38                 int r = l+len-1;
39                 dp[l][r] = INF;
40                 for(int k = 1; k<=r-l+1; k++)
41                 {
42                     int tmp = dp[l+1][l+k-1] + dp[l+k][r] + (k-1)*a[l] + k*(sum[r]-sum[l+k-1]);
43                     dp[l][r] = min(dp[l][r], tmp);
44                 }
45             }
46         }
47         printf("Case #%d: %d\n", ++kase, dp[1][n]);
48     }
49 }
View Code

 

posted on 2017-12-02 15:18  h_z_cong  阅读(171)  评论(0编辑  收藏  举报

导航