多重背包问题

谁都会走 提交于 2020-02-27 06:56:10

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\n",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\n",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次方

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!