• 多重背包问题


    Problem Description

    为了庆贺班级在校运动会上取得全校第一名成绩,班主任决定开一场庆功会,为此拨款购买奖品犒劳运动员。期望拨款金额能购买最大价值的奖品,可以补充他们的精力和体力。

    Input

    输入第一行为T,表示数据组数,对于每组数据的第一行二个数n(n<=500),m(m<=6000),其中n代表希望购买的奖品的种数,m表示拨款金额。
    接下来n行,每行3个数,v、w、s,分别表示第I种奖品的价格、价值(价格与价值是不同的概念)和购买的数量(买0件到s件均可),其中v<=100,w<=1000,s<=10。

    Output

    对于每组数据输出一个数,表示此次购买能获得的最大的价值(注意!不是价格)。

    Sample Input

    1
    5 1000
    80 20 4
    40 50 9
    30 50 7
    40 30 6
    20 20 1

    Sample Output

    1040

    【解析】
    和01差不多 区别在于 每个物品的数量是有限的(或者说是给定的) 每一种物品假设有n[i]件,那么这个物品的决策就是n[i]+1(为什么要加1呢,因为还可以不取这件物品,也算一种决策方式)
    下面的代码是两种方法 第二种为 二进制优化 理解一下
    【代码】
    和01很像,只是1件物品改成的有限件 只需要加一个循环 枚举到底选几件好了
    #include<iostream>
    #include<cstdio>
    using namespace std;
    int pri[10000],val[2000],cnt[2000],f[2000];
    int main()
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d%d%d",&pri[i],&val[i],&cnt[i]);
        }
        for(int i=1;i<=n;i++)
        {
            for(int j=m;j>=0;j--)
            {
                for(int k=0;k<=cnt[i];k++)
                {
                    if(j-k*pri[i]<0)break;
                    f[j]=max(f[j],f[j-k*pri[i]]+k*val[i]);
                }
            }
        }
        printf("%d",f[m]);
        return 0;
    }

    【二进制优化】

    #include<iostream>
    #include<cstdio>
    using namespace std;
    int w[2000],v[2000],c[20000],f[20000],V[2000],W[2000];
    int main()
    {
        int n,m,cnt=0;
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++)
        {
        scanf("%d%d%d",&w[i],&v[i],&c[i]);//价格,价值,数量;
        for(int j=1;j<=c[i];j<<=1)//j<<=1为j=j*2; ------------*(1) 
        {
            V[cnt]=j*v[i];//cnt为情况数目,j为取得件数
            //为i物品取j件时总价值 
            W[cnt++]=j*w[i];
        //    printf("%d %d %d %d
    ",W[cnt-1],V[cnt-1],c[i],j);*(3)
            c[i]-=j;//看下面的数据理解 这里是二进制的分解 
        
        }    
        if(c[i]>0)//如果没有分解完-----------*(2)
        {
            
           V[cnt]=c[i]*v[i];
        W[cnt++]=c[i]*w[i];
    //    printf("%d %d %d %d
    ",i,c[i],V[cnt-1],W[cnt-1]);
        }
        }
        for(int i=0;i<cnt;i++)
        {
            for(int j=m;j>=W[i];j--)
            {
                f[j]=max(f[j],f[j-W[i]]+V[i]);
            }
        }
        printf("%d",f[m]);
         return 0;
    }

    解释一下*所在的位置

    *(1)任何一个数 都可以被分解为 2的n次方的形式

    如 1 2 4 8 16.。。。为2的n的次方前几项 

    则 7=1+2+4. 6=4+2;

    所以我们将第i件物品的个数进行分解为2的n次方的和的形式。

    *(2)当你分解的个数没有分解完时,

    如 (按照程序分解)11=1+2+4;(这里的意思为 11在程序中分解成了 1 2 4)尽管1 2 4这三个数可以 1 2 4 4来组成11;

    可是程序中只保存了 取1个时 取2个时 取4个时,当我们既取1个又取2个,4个 可是没办法取11个全部;

    //具体详细的证明为什么任何数都可以分解成2的n次方就不证了(因为我不会证orz)

    给上面代码的数组加深理解 是怎样分解的呢

    *(3)

    5 1000
    80 20 4
    80 20 4 1
    160 40 3 2
    
    40 50 9
    40 50 9 1
    80 100 8 2
    160 200 6 4
    
    30 50 7
    30 50 7 1
    60 100 6 2
    120 200 4 4
    
    40 30 6
    40 30 6 1
    80 60 5 2
    
    20 20 1
    20 20 1 1
    
    1040

    注意看最后一个数为分解的件数 都为2的n次方

  • 相关阅读:
    python3 操作excel(读操作)
    display:inline-block;产生间隙解决方法
    vedio自定义样式
    angularjs路由
    移动端设置字体px转换rem的脚本
    div在不固定高度的情况下垂直或者水平居中
    css气泡地址和分享地址
    js点击按钮div显示,点击div或者body和按钮,div隐藏
    js倒计时跳转页面
    点击按钮div显示,点击div或者document,div隐藏
  • 原文地址:https://www.cnblogs.com/zzyh/p/6741029.html
Copyright © 2020-2023  润新知