洛谷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盘的乐曲的最大数目。
输入输出样例
4 5 2 4 3 4 2
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 }