珍惜现在,感恩生活 (多重背包)

题意;输入数据首先包含一个正整数C,表示有C组测试用例 每组测试用例的第一行是两个整数n和m(1<=n<=100, 1<=m<=100) 分别表示经费的金额和大米的种类,然后是m行数据,
每行包含3个数p,h和c(1<=p<=20,1<=h<=200,1<=c<=20) 分别表示每袋的价格、每袋的重量以及对应种类大米的袋数。  求能得到的最大重量多重背包

思路:这是一道很模板的多重背包

所以我就对多重背包的dp思路总结一下:

事实上多重背包问题可以看作0/1背包和完全背包的总和

比如给出一个物品 体积w[i] , 数量num[i] , 如果 w[i]*num[i] >= N(总的背包体积/或者余下的体积),那么意味着 我可以随便取这个物品,直到背包装满 或者塞不下这个物品

如果w[i]*num[i] <N ,那么我们就只能取(1~ num[i])个这个物品 ,我们假设其为 num[i]个不同的物品但是其w和v相同 ,然后一个一个取 (所以此时就转化为0/1背包)

当然我们在一个一个取时可以相对优化一下 采用二进制的思想 更快速的取完这个物品

下面这个代码是转用别人的模板我觉得对理解很有意义:https://blog.csdn.net/qq_38984851/article/details/81133840

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define MAX 1000000
using namespace std;
 
int dp[MAX];//存储最后背包最大能存多少
int value[MAX],weight[MAX],number[MAX];//分别存的是物品的价值,每一个的重量以及数量
int bag;
 
void ZeroOnePack(int weight,int value )//01背包
{
    int i;
    for(i = bag; i>=weight; i--)
    {
        dp[i] = max(dp[i],dp[i-weight]+value);
    }
}
void CompletePack(int weight,int value)//完全背包
{
    int i;
    for(i = weight; i<=bag; i++)
    {
        dp[i] = max(dp[i],dp[i-weight]+value);
    }
}
 
void MultiplePack(int weight,int value,int number)//多重背包
{
    if(bag<=number*weight)//如果总容量比这个物品的容量要小,那么这个物品可以直到取完,相当于完全背包
    {
        CompletePack(weight,value);
        return ;
    }
    else//否则就将多重背包转化为01背包
    {
        int k = 1;
        while(k<=number)
        {
            ZeroOnePack(k*weight,k*value);
            number = number-k;
            k = 2*k;//这里采用二进制思想(二进制与十进制的转化)(快速幂也是这个思路)
            //而不是一个一个的减 
        }
        ZeroOnePack(number*weight,number*value);
    }
}
int main()
{
    int t;
    cin>>t;
    while(t--){
         int n;
        while(~scanf("%d%d",&bag,&n))
        {
            int i,sum=0;
            for(i = 0; i<n; i++)
            {
                scanf("%d",&weight[i]); 
                
                scanf("%d",&value[i]);//输入价值  此题没有物品的重量,可以理解为体积和价值相等
                   scanf("%d",&number[i]);//输入数量
            }
            memset(dp,0,sizeof(dp));
            for(i = 0; i<n; i++)
            {
                MultiplePack(weight[i],value[i],number[i]);//调用多重背包,注意穿参的时候分别是
                //重量,价值和数量
            }
            cout<<dp[bag]<<endl;
        }
        return 0;    
    }  
}

然后这是另一种思路的代码:(就是尝试对每一种都一个一个取)

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int v[110],w[110],ans[110],dp[110][110];
int main()
{
    int t,n,m,i,j,k;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&m,&n); //m是经费,n种大米
        for(i=0;i<n;i++)
            scanf("%d%d%d",&w[i],&v[i],&ans[i]);//w价格,v是每袋的重量,ans是袋数
        memset(dp,0,sizeof(dp));  //初始化
        for(i=0;i<n;i++)   //n种大米
        {
            for(j=0;j<=m;j++)    //剩余j元最多能买多少
            {
                if(j<w[i])        //剩余的钱不能买这一袋
                    dp[i+1][j]=dp[i][j];
                else
                {
                    for(k=0;k<=ans[i]&&w[i]*k<=j;k++)   //这种大米,从买0袋开始尝试买多少
                       dp[i+1][j]=max(dp[i+1][j],dp[i][j-k*w[i]]+k*v[i]);
                       //             原本的花费        这种大米买k袋
                }
            }
        }
        printf("%d
",dp[n][m]);
    }

}
原文地址:https://www.cnblogs.com/Tianwell/p/11218204.html