Jin Ge Jin Qu hao UVA - 12563

29 篇文章 0 订阅
14 篇文章 0 订阅

UVA-12563

 题意:求在给定时间内,最多能唱多少歌曲,在最多歌曲的情况下,使唱的时间最长。

 思路引用原博,侵删):    该题类似于01背包问题,可用01背包问题的解题思路来求,每个歌曲相当于物品,歌曲的长度相等于物品重量,每个歌曲的“价值”为1。由于金歌劲曲时间最长,所以最后要留至少1秒时间开始唱金歌劲曲,所以计算t-1时间内最多唱的歌曲和时间,最终答案为歌曲数加1,时间加上金歌劲曲的时间。这里我使用滚动数组计算这个值, 用time_left记录t-1。

      需要注意的是,由于要求是连续唱歌,且要求在最多歌曲数的情况下时间最长,如果按普通的背包存储,很难得到最长时间,因为dp[len] 只存储了最多的歌曲数,但并不知道这些歌曲到底唱了多少时间。假设最多歌曲数为ans, 唱ans首歌曲最少时间为time_x, 那么数组中从dp[time_x]到dp[len]都等于num,我们无法得知唱ans首歌的最大时间。比如说time_left = 10, w[1] = 5, w[2] = 8, 那么dp[5] 到 dp[10] 都等于1, 无法知道唱从5到10哪个是唱1首歌的最长时间。如何处理呢?

  这里需要用到一个技巧:对决策进行一定的限定!在计算某个时间最多唱的歌曲时,必须是该时间内恰好唱完这些歌,时间多了不行。即dp[i]表示的是在i 的时间恰好用完的情况下最多能唱多少首歌。比如上面的例子只有dp[5] 和dp[8]等于1,其他的都等于0。这样的话处理时先算出最多唱的歌曲数 ans,然后从k = ans开始遍历数组dp, 第一个等于ans的就是在最多歌曲情况下的最长时间。

#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<sstream>
#include<map>
#include<stack>
using namespace std;
int w[100],dp[1000000],n;
int time_left;
int main(){
    int T,kase = 0;
    cin>>T;
    while(T--){
        int ans = 0;//用于保存最多歌曲数目
        memset(dp, 0, sizeof(dp)); //dp[i]指还剩i秒时可 刚好播放 的曲目个数
        printf("Case %d: ",++kase);
        cin>>n>>time_left;
        time_left--; //留一秒播放劲歌金曲
        for(int i = 1;i<=n;i++) cin>>w[i];
        for(int i = 1;i<=n;i++){
            for(int v = time_left;v>=w[i];v--){
                if(dp[v - w[i]]>=1||v==w[i]){ //在时间还剩v时,可以播放第i首歌
                    dp[v] = max(dp[v],dp[v-w[i]]+1);
                    ans = max(ans,dp[v]);
                }
            }
        }
        int k;
        for(k = time_left;;k--) if(dp[k]==ans) break; //找到第一个可以恰好播放ans首歌的最大时间k
        if(ans==0) cout<<ans+1<<" "<<678<<endl;
        else cout<<ans+1<<" "<<k+678<<endl;
    }
    return 0;
}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值