(0-1背包)Jin Ge Jin Qu,UVa 12563

部分转自:https://blog.csdn.net/u013480600/article/details/40376143
UVA 12563 Jin Ge Jin Qu hao(01背包变形:两个条件最优化)

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4008

题意:

   KTV里面有n首歌曲你可以选择,每首歌曲的时长都给出了. 对于每首歌曲,你最多只能唱1遍. 现在给你一个时间限制t (t<=10^9) , 问你在最多t-1秒的时间内可以唱多少首歌曲num , 且最长唱歌时间是多少time (**time必须<=t-1**) ? 最终输出num+1 和 time+678 即可.

   注意: 你需要优先让歌曲数目最大的情况下,再去选择总时长最长的.

分析:

   其实本题本质上就是一个标准的01背包问题. 问你<=t-1时间内最多可以选择哪些歌曲使得 (数据1,数据2) 最优. 这里的**数据1**是歌曲数目, **数据2**是歌曲总时长, 且**数据1优先.**

   一般我们做的01背包问题都是问你<=t-1的时间内, 最多选择哪些歌曲使得歌曲数目最多 **或** 总时间最长. 但是本题需要同时考虑两个最优条件, 那么该怎么做呢?

   我们令dp[i][j]==x 表示当决策完全前i个物品后(选或不选), 所选的总歌曲时长<=j时, 所得到的**最优状态**为x. (**这里的x就不是平时我们所说的最长时间或最多歌曲数目了**)

   怎么理解最优状态为x这个事实呢? 假设有两种选择前i个歌曲的方法能使得**决策完前i个物品且总时长<=j时的状态**分别为x1 和x2.

   那么如果x1状态的歌曲数目> x2状态的歌曲数目, 那么明显x1状态更优. 所以dp[i][j]应==x1.

   如果x1状态的歌曲数目与x2的相等, 但是x2状态的时长 > x1状态时长, 那么此时x2状态更优. 所以dp[i][j]应==x2.

   经过上面的分析,我们可以用一个(具有歌曲数和总时长双属性的)结构体来表示一个状态. 且可以得到下面状态转移公式:

   dp[i][j] = **最优( **dp[i-1][j] **, ** 在dp[i-1][j-t[i]]的基础上选择第i首歌后得到的新状态tmp **)**

   所有dp初始化为0即可. 最终我们所求为dp[n][max_time]

   最后还有一个问题就是t<=10^9.我们不可能循环判断j到10^9\. 其实一共50首歌曲, 每首歌曲最多180秒, 所以我们求出所有歌曲的时长和sum(sum<=50*180==9000).

   如果t-1>=sum, 那么明显所有歌曲都能被选一遍.

   如果t-1<sum,那么明显我们需要遍历到dp[i][t-1]为止.

   程序实现用的滚动数组,所以dp只有[j]这一维.

这里贴三份代码

这份主要在于结构体的使用

#include<cstdio>  
#include<cstring>  
#include<algorithm>  
using namespace std;  
const int maxn=180*50+5;  
  
int n,max_t;  
int t[50+5]; //每首歌曲的时间  
struct Node  
{  
    int num; //总歌曲数  
    int time;//歌总时间  
    bool operator<(const Node &rhs)const//判断是否更优  
    {  
        return num<rhs.num || (num==rhs.num && time<rhs.time);  
    }  
}dp[maxn];  
  
int main()  
{  
    int T;  
    scanf("%d",&T);  
    for(int kase=1;kase<=T;kase++)  
    {  
        printf("Case %d: ",kase);  
  
        scanf("%d%d",&n,&max_t);  
        memset(dp,0,sizeof(dp));  
  
        int sum=0;//所有歌曲总时长  
        for(int i=1;i<=n;i++)  
        {  
            scanf("%d",&t[i]);  
            sum +=t[i];  
        }  
        //max_t是我们最大需要考虑的时间  
        max_t = min(sum,max_t-1);  
        //注意max_t==sum和max_t==0时的情况.  
  
        int ans=0;  
        for(int i=1;i<=n;i++)  
        {  
            for(int j=max_t;j>=t[i];j--)  
            {  
                Node tmp;//tmp表示当选择第i首歌时的情况  
                tmp.num  = dp[j-t[i]].num+1;  
                tmp.time = dp[j-t[i]].time+t[i];  
                if(dp[j]<tmp)//tmp更优  
                {  
                    dp[j]=tmp;  
                }  
            }  
        }  
        printf("%d %d\n",dp[max_t].num+1,dp[max_t].time+678);  
    }  
    return 0;  
}  

这份在于滚动数组的使用

#include<cstdio> 
#include<iostream> 
#include<cstring> 
#include<algorithm> 
using namespace std; 
#define maxn 100000 
int dp[maxn],d[maxn]; 
int main() { 
        // freopen("input.txt","r",stdin); 
    int q; 
    scanf("%d",&q); 
    for(int w=1;w<=q;w++) 
    { 
        int n,t,i,j,a; 
        scanf("%d%d",&n,&t); 
        memset(dp,0,sizeof(dp)); 
        memset(d,0,sizeof(d)); 
        for(i=1;i<=n;i++) { //正序
            scanf("%d",&a); 
            for(j=t-1;j>=a;j--) {//逆序
                if(dp[j]<dp[j-a]+1){
                    dp[j]=dp[j-a]+1; 
                    d[j]=a+d[j-a] ;
                } else if(dp[j]==dp[j-a]+1){ 
                        d[j]=max(d[j-a]+a,d[j]); 
                                }
                            } 
          } 
          printf("Case %d: %d %d\n",w,dp[t-1]+1,d[t-1]+678); 
      } 
      return 0; 
}

第三份就是照着书上二维实现的代码

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxn = 100;
int d[maxn][maxn+10000];
int path[maxn][maxn+10000];//前i个在第j秒最多能唱的歌曲数目;
int w[maxn];
int n,t;

int main(){
    int cont;
    scanf("%d", &cont);
    int cnt = 0;
    while(cont--){
        cnt++;
        scanf("%d %d", &n ,&t);
    for(int i = 1; i <= n; i++){
        scanf("%d", &w[i]);
    }
    memset(path, 0 , sizeof(path));
    memset(d,0,sizeof(d));
    for(int i = n; i>= 1; i--){ 
        for(int j = 0; j <= t-1; j++){
            d[i][j] = d[i+1][j];
            path[i][j] = path[i+1][j];
            if(j >= w[i]){
                if(d[i][j]<d[i+1][j-w[i]]+1)
                {
                    d[i][j]=d[i+1][j-w[i]]+1;
                    path[i][j]=path[i+1][j-w[i]]+w[i];
                }
                else if(d[i][j]==d[i+1][j-w[i]]+1)
                {
                    path[i][j]=max(path[i][j],path[i+1][j-w[i]]+w[i]);
                }
            }
        }
    }
    
    printf("Case %d: %d %d\n", cnt ,d[1][t-1]+1, path[1][t-1]+678); 
    
    }
    return 0;
}

其中,第一二种方法耗时小,第三种耗时大。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352

推荐阅读更多精彩内容