洛谷P2736 “破锣摇滚”乐队 Raucous Rockers

P2736 “破锣摇滚”乐队 Raucous Rockers

题目描述

你刚刚继承了流行的“破锣摇滚”乐队录制的尚未发表的N(1 <= N <= 20)首歌的版权。你打算从中精选一些歌曲,发行M(1 <= M <= 20)张CD。每一张CD最多可以容纳T(1 <= T <= 20)分钟的音乐,一首歌不能分装在两张CD中。

不巧你是一位古典音乐迷,不懂如何判定这些歌的艺术价值。于是你决定根据以下标准进行选择:

1.歌曲必须按照创作的时间顺序在所有的CD盘上出现。(注:第i张盘的最后一首的创作时间要早于第i+1张盘的第一首)

2.选中的歌曲数目尽可能地多

输入输出格式

输入格式:

第一行: 三个整数:N, T, M.

第二行: N个整数,分别表示每首歌的长度,按创作时间顺序排列。

输出格式:

一个整数,表示可以装进M张CD盘的乐曲的最大数目。

输入输出样例

输入样例#1:
4 5 2
4 3 4 2
输出样例#1:
3

说明

题目翻译来自NOCOW。

USACO Training Section 3.4

 

【题解】

“ 设n首歌曲按照创作顺序排序后的长度为long[1..n],则动态规划的状态表示描述为:
     g[i, j, k],(0≤i≤n,0≤j≤m,0≤k<t), 表示前i首歌曲,用j张唱片另加k分钟来录制,最多可以录制的歌曲数目。
状态转移方程为:
当k≥long[i],i≥1时:
g[i, j, k]=max{g[i-1,j,k-long[i]]+1,g[i-1,j,k]}
当k<long[i],i≥1时:
g[i, j, k]=max{g[i-1,j-1,t-long[i]]+1,g[i-1,j,k]}
规划的边界条件为:
当0≤j≤m, 0≤k<t时:g[0,j,k]=0;
问题的最优解为:g[n,m,0]。

 

改进的状态表示描述为:
     g[i,j]=(a, b),0≤i≤n,0≤j≤i,0≤a≤m,0≤b≤t,表示在前i首歌曲中选取j首录制所需的最少唱片为:a张唱片另加b分钟。
状态转移方程为:
g[i, j]=min{g[i-1,j],g[i-1,j-1]+long[i]}
其中(a, b)+long[i]=(a’, b’)的计算方法为:
当b+long[i] ≤t时: a’=a;       b’=b+long[i];
当b+long[i] >t时: a’=a+1;   b’=long[i];
规划的边界条件:
当0≤i≤n时,g[i,0]=(0,0)
题目所求的最大值是:answer=max{k| g[n, k]≤(m-1,t)} ”

——朱全民

注意,可能唱盘长度 > k倍的唱片可容纳量

 

 

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstdlib>
 4 #include <cstring>
 5 #define max(a, b) ((a) > (b) ? (a) : (b))
 6 
 7 inline void read(int &x)
 8 {
 9     x = 0;char ch = getchar(), c = ch;
10     while(ch < '0' || ch > '9')c = ch, ch = getchar();
11     while(ch <= '9' && ch >= '0')x = x * 10 + ch - '0', ch = getchar();
12     if(c == '-')x = -x;
13 }
14 
15 const int INF = 0x3f3f3f3f;
16 const int MAXN = 20 + 5;
17 
18 struct Node
19 {
20     int num,t;
21     Node(){num = t = INF;}
22 }dp[MAXN][MAXN];
23 
24 int n,t,m,len[MAXN];
25 
26 int main()
27 {
28     read(n), read(t), read(m);
29     for(register int i = 1;i <= n;++ i)
30     {
31         read(len[i]);
32         dp[i][0].t = dp[i][0].num = 0;
33     }
34     dp[0][0].t = dp[0][0].num = 0;
35     Node tmp;
36     for(register int i = 1;i <= n;++ i)
37         for(register int j =  1;j <= n;++ j)
38         {
39             tmp = dp[i - 1][j - 1];
40             if(len[i] > t - tmp.t)++ tmp.num, tmp.t = len[i];
41             else tmp.t += len[i];
42             while(tmp.t >= t)++tmp.num, tmp.t -= t, tmp.t = max(0, tmp.t);
43             if(dp[i - 1][j].num < tmp.num)
44                 dp[i][j] = dp[i - 1][j];
45             else if(dp[i - 1][j].num == tmp.num)
46                 if(dp[i - 1][j].t < tmp.t)
47                     dp[i][j] =  dp[i - 1][j];
48                 else
49                     dp[i][j] = tmp;
50             else
51                 dp[i][j] = tmp;
52         }
53     int ans = 0;
54     for(register int i = 1;i <= n;++ i)
55         if((dp[n][i].num < m) || (dp[n][i].num == m && !dp[n][i].t))
56             ans = i;
57     printf("%d", ans);
58     return 0;
59 } 
洛谷P2736

 

 

 

posted @ 2017-09-13 16:24  嘒彼小星  阅读(179)  评论(0编辑  收藏  举报