Jin Ge Jin Qu - UVa 12563
例题9-5 劲歌金曲(Jin Ge Jin Qu [h]ao, Rujia Liu's Present 6, UVa 12563)
#dp #二维dp #01背包 #T3
如果问一个麦霸:“你在KTV里必唱的曲目有哪些?”得到的答案通常都会包含一首“神曲”:古巨基的《劲歌金曲》。
为什么呢?一般来说,KTV不会在“时间到”的时候鲁莽地把正在唱的歌切掉,而是会等它放完。例如,在还有 \(15\) 秒时再唱一首 \(2\) 分钟的歌,则实际上多唱了 \(105\) 秒。但是融合了 \(37\) 首歌曲的《劲歌金曲》长达 \(11\) 分 \(18\) 秒(5),如果唱这首,相当于多唱了\(663\)秒!
假定你正在唱KTV,还剩 \(t\) 秒时间。你决定接下来只唱你最爱的 \(n\) 首歌(不含《劲歌金曲》)中的一些,在时间结束之前再唱一个《劲歌金曲》,使得唱的总曲目尽量多(包含《劲歌金曲》),在此前提下尽量晚的离开KTV。
输入\(n(n≤50),t(t≤10^9)\)和每首歌的长度(保证不超过 \(3\) 分钟),输出唱的总曲目以及时间总长度。输入保证所有 \(n+1\) 首曲子的总长度严格大于 \(t\)。
Sample Input
2
3 100
60 70 80
3 100
30 69 70
Sample Output
Case 1: 2 758
Case 2: 3 777
思路
求能唱的数量最多, 且最多时求时间最长的, 显然是二维DP和这题很像 [[宠物小精灵之收服]]。
因为最后肯定是留至少1秒点金曲是最优, 故这里直接把输入的t减去1, 输出时判断一下, 如果是-1说明一个都点不了, 都输出0, 否则就在输出时把金曲加上。
其他就是正常的01背包了。
先求数量最多的, 然后逆序遍历求用时最多的。
滚动数组优化
状态表示: f[j]
恰好用时为j的最优选法
状态计算: f[j] = max(f[j], f[j - t] + 1)
注意全初始化为 -0x3f 负无穷, 因为这里求的是恰好为j。
题目给的t最大是1e9, 但实际上用不到这么多, 题目有说一首歌最多3分钟, 故总时间最大也就 180*n + 678
。
代码
const int N = 55, T = 1e4 + 10;
int a[T];
int n, m;
int main()
{
int K;
cin >> K;
for (int kase = 1; kase <= K; kase++)
{
mem(a, -0x3f);
a[0] = 0;
cin >> n >> m;
m -= 1;
int res = 0;
for (int i = 1; i <= n; i++)
{
int t;
cin >> t;
for (int j = m; j >= t; j--)
{
a[j] = max(a[j], a[j - t] + 1);
}
}
for (int i = 0; i <= m; i++)
res = max(res, a[i]);
int time = 0;
for (int i = m; i >= 0; i--)
if (a[i] == res)
{
time = i;
break;
}
if (m != -1)
{
printf("Case %d: %d %d\n", kase, res + 1, time + 678);
}
else
printf("Case %d: %d %d\n", kase, 0, 0);
}
}