草庐IT

背包问题(5):混合背包

I am a teacher! 2023-03-28 原文

        混合背包就是将前面三种基本的背包问题叠加成较复杂的问题。也就是说,有的物品只可以取一次(0/1背包),有的物品可以取无限次(完全背包),有的物品可以取的次数有一个上限(多重背包)。

        0/1背包与完全背包的混合比较简单。如果只有两类物品:一类物品只能取一次,另一类物品可以取无限次,那么只需在对每个物品应用转移方程时,根据物品的类别选用逆序(0/1背包)或顺序(完全背包)的循环即可。

       一般可以编写如下的循环。

​       for (i=1;i<=N;i++)                                            // 装入背包的物品个数为N

       {

              if  (第i件物品只能取1次)                         //  0/1背包

                      for (j=V;j>=W[i];j--)                         //  0/1背包按由大到小顺序枚举重量

                           f[j]=max(f[j],f[j-W[i]]+P[i]);

            else                                                         // 第i件物品可以取无数次,完全背包

                      for ( j=W[i];j<=V;j++)                     // 完全背包按由小到大顺序枚举重量

                          f[j]=max(f[j],f[j-W[i]]+P[i]);   

        }

        如果再加上多重背包,一般可以编写如下循环程序。

​         for (i=1;i<=N;i++)           // 装入背包的物品个数为N

        {

               if  (第i件物品只能取1次)                   //  0/1背包

              {

                      for (j=V;j>=W[i];j--)  

                            f[j]=max(f[j],f[j-W[i]]+P[i]);

              }

               else  if  (第i件物品可以取无数次)     // 完全背包

              {

                      for ( j=W[i];j<=V;j++)    

                             f[j]=max(f[j],f[j-W[i]]+P[i]);  

             }

            else  if  (第i件物品可以取c[i]次)           // 多重背包

            {

             for (j=V; j>=0; j--)  

                 for (k=0; k<=c[i] &&  k*W[i] <=j; k++)

                      f[j] = max( f[j], f[j - k * W[i]] + k *P[i]);

             }

        } 

【例1】最多的糖果数

问题描述

今天是CRB的生日。他妈妈决定给她可爱的儿子买很多礼物。

她带着M元(货币单位)去了最近的商店。

商店里有N种礼物。买一件第i种礼物要花Wi元。

但由于商店的老板是她的朋友,如果她买了x(x>0)件第i种礼物,花费x×Wi元,老板会送给她Ai×x+Bi颗糖果。

她想得到最多的糖果。你的任务是帮助她。

输入

有多个测试用例。输入的第一行包含一个整数T(1≤ T≤ 20),表示测试用例的数量。

对于每个测试用例:第一行包含两个整数M(1≤ M≤ 2000)和N(1≤ N≤ 1000)。接下来是N行,第i行包含三个空格分隔的整数Wi(1≤Wi≤2000)、Ai和Bi(0≤Ai,Bi≤2000)。

输出

对于每个测试用例,输出她能获得的最大糖果。

输入样例

1

100 2

10 2 1

20 1 1

输出样例

21

        (1)编程思路。

         对于第i件商品,如果只买1个,得到的价值是Ai+Bi;如果在买1个的基础上再买,得到的价值就是Ai。也就是说,第i件商品,除了第一次得到Ai+Bi颗糖果,以后购买都只得到Ai颗糖果,这样可以将第i件商品看成两种商品,其中两种商品的购买代价都是Wi,第一种的价值是Ai+Bi,但是只允许买一次;第二种的价值是Ai,可以无限次购买。第一种商品用0/1背包处理,第二种商品用完全背包处理,并且先进行0/1背包,再进行完全背包。由于第一种商品的价值Ai+Bi大于或等于第二种商品的价值,付出相同的代价,价值大的肯定会被先考虑,也就是在用完全背包处理第二种商品时,第一种商品肯定已经被添加到背包里了。

        (2)源程序。

#include <stdio.h>
#include <string.h>
int max(int a,int b)
{
    return a>b?a:b;
}
int main()
{
    int f[2005];
    int t;
    scanf("%d", &t);
    while (t--)
    {
        int m,n;
        scanf("%d%d", &m, &n);
        memset(f,0,sizeof(f));
        int w, a, b;
        int i,j;
        for (i = 1; i <=n; i++)
        {
            scanf("%d%d%d", &w, &a, &b);
            for (j= m; j >= w; j--)    // 每个商品的第1件只买1次,0/1背包
                f[j] = max(f[j], f[j-w] + a + b);
            for (j = w; j<=m; j++)    // 每个商品之后可购买次数不限,完全背包
                f[j] = max(f[j], f[j-w] + a);
        }
        int ans = 0;
        for (i=0; i<=m; i++)
            ans = max(ans, f[i]);
        printf("%d\n", ans);
    }
    return 0;
}

        将上面的源程序提交给HDU题库HDU 5410 CRB and His Birthday(http://acm.hdu.edu.cn/showproblem.php?pid=5410),测评结果为Accepted

 【例2】樱花

题目描述

爱与愁大神后院里种了n棵樱花树,每棵都有美学值 Ci (0≤Ci≤200)。爱与愁大神在每天上学前都会来赏花。爱与愁大神可是生物学霸,他懂得如何欣赏樱花:一种樱花树看一遍过,一种樱花树最多看 Ai (0≤Ai≤100)遍,一种樱花树可以看无数遍。但是看每棵樱花树都有一定的时间Ti (0≤Ti≤100)。爱与愁大神离去上学的时间只剩下一小会儿了。求解看哪几棵樱花树能使美学值最高且爱与愁大神能准时(或提早)去上学。

输入

共 n+1行:

第1行:现在时间 Ts(几时:几分),去上学的时间 Te(几时:几分),爱与愁大神院子里有几棵樱花树 n。这里的Ts,Te格式为:hh:mm,其中0≤hh≤23,0≤mm≤59,且 hh,mm,n均为正整数。开始时间距离结束时间不超过 1000分钟,n≤10000。

第2行到第n+1 行,每行三个正整数:看完第i棵树的耗费时间Ti,第i棵树的美学值Ci,看第i棵树的次数Pi(Pi =0 表示无数次,Pi是其他数字表示最多可看的次数Pi)。

输出

只有一个整数,表示最大美学值。

输入样例

6:50 7:00 3

2 1 0

3 3 1

4 5 4

输出样例

11

样例解释

赏第一棵樱花树一次,赏第三棵樱花树2次。

        (1)编程思路1。

        一种樱花树看一遍过(0/1背包),一种樱花树最多看 Ai (0≤Ai≤100)遍(多重背包),一种樱花树可以看无数遍(完全背包)。

        设现在时间为h1:m1,去上学的时间为h2:m2,则limt=h2*60+m2-(h1*60+m1)就是背包的容量,将看樱花耗费的时间ti看成物品的重量,樱花的美学值看成物品的价值,按上面的介绍,3种背包混合时分别处理即可。

        (2)源程序1。

#include <stdio.h>
#include <string.h>
int max(int a,int b)
{
    return a>b?a:b;
}
int main()
{
    int n,h1,m1,h2,m2;
    scanf("%d:%d %d:%d %d",&h1,&m1,&h2,&m2,&n);
    int limt=h2*60+m2-(h1*60+m1);
    int f[1005]={0};
    int i,j,k;
    for (i=1;i<=n;i++)
    {
        int t,c,p;
        scanf("%d%d%d",&t,&c,&p);
        if  (p==1)          //   看一次,0/1背包
        {
            for (j=limt;j>=t;j--)
                f[j]=max(f[j],f[j-t]+c);
        }
        else  if  (p==0)    // 看无数次,完全背包
        {
            for (j=t;j<=limt;j++)
                  f[j]=max(f[j],f[j-t]+c);
        }
        else               // 看pi次,多重背包
        {
             for (j=limt; j>=0; j--)
                 for (k=0; k<=p &&  k*t <=j; k++)
                      f[j] = max( f[j], f[j-k*t] + k *c);
        }
    }
    printf("%d\n",f[limt]);
    return 0;
}

        (3)编程思路2。

        可以将完全背包和多重背包均转化为0/1背包求解。樱花可以看无数遍,实际上由于背包容量的限制,最多也只能看limt/t遍,因此完全背包可看成一个物品取limt/t的多重背包,采用二进制拆分优化的方法将多重背包转换为0/1背包统一求解。

        (4)源程序2。

#include <stdio.h>
#include <string.h>
int t[1000005],c[1000005];
int main()
{
    int n,h1,m1,h2,m2;
    scanf("%d:%d %d:%d %d",&h1,&m1,&h2,&m2,&n);
    int limt=h2*60+m2-(h1*60+m1);
    int i,j;
    int cnt=0;
    for (i=1;i<=n;i++)
    {
        int a,b,p;
        scanf("%d%d%d",&a,&b,&p);
        if (p==0) p=p=limt/a;
        for (j=1;j<=p;j<<=1)
        {
            t[++cnt]=j*a;
            c[cnt]=j*b;
            p-=j;
        }
        if (p)
        {
            t[++cnt]=a*p;
            c[cnt]=b*p;
        }
    }
    int f[1005];
    memset(f,0,sizeof(f));
    for (i=1;i<=cnt;i++)
        for (j=limt;j>=t[i];j--)
            if (f[j]<f[j-t[i]]+c[i]) f[j]=f[j-t[i]]+c[i];
    printf("%d\n",f[limt]);
    return 0;
}

        将上面的源程序提交给洛谷题库P1833 樱花(https://www.luogu.com.cn/problem/P1833),测评结果为Accepted

练习题

1.P1782 旅行商的背包(https://www.luogu.com.cn/problem/P1782

#include <stdio.h>
long long max(long long a,long long b)
{
    return a>b?a:b;
}
int main()
{
    long long n,m,C;
    scanf("%lld%lld%lld",&n,&m,&C);
    long long f[10005]={0};
    long long i,j,k;
    for (i=1;i<=n;i++)          // n种物品多重背包
    {
        long long v,w,d;
        scanf("%lld%lld%lld",&v,&w,&d);
        if (v*d>=C)
        {
           for (j=v;j<=C;j++)
              f[j]=max(f[j],f[j-v]+w);
        }
        else
        {
           k=1;
           while (k<d)
           {
              for (j=C;j>=k*v;j--)
                   f[j]=max(f[j],f[j-k*v]+k*w);
              d-=k;
              k*=2;
           }
           if (d>0)
           {
               for (j=C;j>=d*v;j--)
                  f[j]=max(f[j],f[j-d*v]+d*w);
           }
        }
    }
    for (i=1;i<=m;i++)         // m件奇货是0/1背包
    {
        long long a,b,c;
        scanf("%lld%lld%lld",&a,&b,&c);
        for (j=C;j>=0;j--)
            for (k=0;k<=j;k++)
                f[j]=max(f[j],f[j-k]+(a*k+b)*k+c);
    }
    printf("%lld\n",f[C]);
    return 0;
}
View Code

2.P2851 [USACO06DEC]The Fewest Coins G(https://www.luogu.com.cn/problem/P2851)

#include <stdio.h>
#include <string.h>
#define INF 0x3f3f3f3f
int min(int a,int b)
{
    return a<b?a:b;
}
int main()
{
    int f1[25000];     // 约翰对不同金额所付的最少硬币数量
    int f2[25000];     // 店家对不同金额找零的最少硬币数量
    int n,t;
    scanf("%d%d",&n,&t);
    int sum=0;
    int i,j;
    int c[105],v[105];
    for (i=0;i<n;i++)
    {
         scanf("%d",&v[i]);
         if (sum<v[i]) sum=v[i];
    }
    for (i=0;i<n;i++)
         scanf("%d",&c[i]);
    sum=sum*sum+t+1;
    memset(f1,INF,sizeof(f1));
    memset(f2,INF,sizeof(f2));
    f1[0]=0;
    f2[0]=0;
    for (i=0;i<n;i++)
    {
        for (j=v[i];j<=sum;j++)      // 店家找零是完全背包
        {
            f2[j]=min(f2[j],f2[j-v[i]]+1);
        }
        if (c[i]*v[i]>=sum)          // 约翰付账是多重背包
        {
           for (j=v[i];j<=sum;j++)
              f1[j]=min(f1[j],f1[j-v[i]]+1);
        }
        else
        {
           int k=1;
           int temp=c[i];
           while (k<temp)
           {
              for (j=sum;j>=k*v[i];j--)
                   f1[j]=min(f1[j],f1[j-k*v[i]]+k);
              temp-=k;
              k*=2;
           }
           for (j=sum;j>=temp*v[i];j--)
              f1[j]=min(f1[j],f1[j-temp*v[i]]+temp);
        }
    }
    int res=INF;
    for (i=t;i<=sum;i++)
    {
         res=min(res,f1[i]+f2[i-t]);
    }
    if (res==INF)
        printf("-1\n");
    else
        printf("%d\n",res);
    return 0;
}
View Code
背包背包问题spanstylecolorC语言

有关背包问题(5):混合背包的更多相关文章

  1. ruby - 在 64 位 Snow Leopard 上使用 rvm、postgres 9.0、ruby 1.9.2-p136 安装 pg gem 时出现问题 - 2

    我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po

  2. ruby - 通过 rvm 升级 ruby​​gems 的问题 - 2

    尝试通过RVM将RubyGems升级到版本1.8.10并出现此错误:$rvmrubygemslatestRemovingoldRubygemsfiles...Installingrubygems-1.8.10forruby-1.9.2-p180...ERROR:Errorrunning'GEM_PATH="/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/ruby-1.9.2-p180@global:/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/rub

  3. ruby-on-rails - 在混合/模块中覆盖模型的属性访问器 - 2

    我有一个包含模块的模型。我想在模块中覆盖模型的访问器方法。例如:classBlah这显然行不通。有什么想法可以实现吗? 最佳答案 您的代码看起来是正确的。我们正在毫无困难地使用这个确切的模式。如果我没记错的话,Rails使用#method_missing作为属性setter,因此您的模块将优先,阻止ActiveRecord的setter。如果您正在使用ActiveSupport::Concern(参见thisblogpost),那么您的实例方法需要进入一个特殊的模块:classBlah

  4. ruby - 通过 RVM (OSX Mountain Lion) 安装 Ruby 2.0.0-p247 时遇到问题 - 2

    我的最终目标是安装当前版本的RubyonRails。我在OSXMountainLion上运行。到目前为止,这是我的过程:已安装的RVM$\curl-Lhttps://get.rvm.io|bash-sstable检查已知(我假设已批准)安装$rvmlistknown我看到当前的稳定版本可用[ruby-]2.0.0[-p247]输入命令安装$rvminstall2.0.0-p247注意:我也试过这些安装命令$rvminstallruby-2.0.0-p247$rvminstallruby=2.0.0-p247我很快就无处可去了。结果:$rvminstall2.0.0-p247Search

  5. ruby - Fast-stemmer 安装问题 - 2

    由于fast-stemmer的问题,我很难安装我想要的任何ruby​​gem。我把我得到的错误放在下面。Buildingnativeextensions.Thiscouldtakeawhile...ERROR:Errorinstallingfast-stemmer:ERROR:Failedtobuildgemnativeextension./System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/rubyextconf.rbcreatingMakefilemake"DESTDIR="cleanmake"DESTDIR=

  6. ruby - 安装 Ruby 时遇到问题(无法下载资源 "readline--patch") - 2

    当我尝试安装Ruby时遇到此错误。我试过查看this和this但无济于事➜~brewinstallrubyWarning:YouareusingOSX10.12.Wedonotprovidesupportforthispre-releaseversion.Youmayencounterbuildfailuresorotherbreakages.Pleasecreatepull-requestsinsteadoffilingissues.==>Installingdependenciesforruby:readline,libyaml,makedepend==>Installingrub

  7. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  8. ruby-on-rails - 简单的 Ruby on Rails 问题——如何将评论附加到用户和文章? - 2

    我意识到这可能是一个非常基本的问题,但我现在已经花了几天时间回过头来解决这个问题,但出于某种原因,Google就是没有帮助我。(我认为部分问题在于我是一个初学者,我不知道该问什么......)我也看过O'Reilly的RubyCookbook和RailsAPI,但我仍然停留在这个问题上.我找到了一些关于多态关系的信息,但它似乎不是我需要的(尽管如果我错了请告诉我)。我正在尝试调整MichaelHartl'stutorial创建一个包含用户、文章和评论的博客应用程序(不使用脚手架)。我希望评论既属于用户又属于文章。我的主要问题是:我不知道如何将当前文章的ID放入评论Controller。

  9. 【高数】用拉格朗日中值定理解决极限问题 - 2

    首先回顾一下拉格朗日定理的内容:函数f(x)是在闭区间[a,b]上连续、开区间(a,b)上可导的函数,那么至少存在一个,使得:通过这个表达式我们可以知道,f(x)是函数的主体,a和b可以看作是主体函数f(x)中所取的两个值。那么可以有,  也就意味着我们可以用来替换 这种替换可以用在求某些多项式差的极限中。方法: 外层函数f(x)是一致的,并且h(x)和g(x)是等价无穷小。此时,利用拉格朗日定理,将原式替换为 ,再进行求解,往往会省去复合函数求极限的很多麻烦。使用要注意:1.要先找到主体函数f(x),即外层函数必须相同。2.f(x)找到后,复合部分是等价无穷小。3.要满足作差的形式。如果是加

  10. SPI接收数据异常问题总结 - 2

    SPI接收数据左移一位问题目录SPI接收数据左移一位问题一、问题描述二、问题分析三、探究原理四、经验总结最近在工作在学习调试SPI的过程中遇到一个问题——接收数据整体向左移了一位(1bit)。SPI数据收发是数据交换,因此接收数据时从第二个字节开始才是有效数据,也就是数据整体向右移一个字节(1byte)。请教前辈之后也没有得到解决,通过在网上查阅前人经验终于解决问题,所以写一个避坑经验总结。实际背景:MCU与一款芯片使用spi通信,MCU作为主机,芯片作为从机。这款芯片采用的是它规定的六线SPI,多了两根线:RDY和INT,这样从机就可以主动请求主机给主机发送数据了。一、问题描述根据从机芯片手

随机推荐