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);
    }
}
posted @ 2023-05-11 21:03  EdwinAze  阅读(39)  评论(0编辑  收藏  举报