多重背包问题

/* 
    Name: 多重背包问题 
    Copyright:  
    Author:  
    Date: 28-07-17 10:24 
    Description:  
多重背包问题:在n种物品中选取若干件(第i种物品最多选N[i]次)放在容量为c的背包里,分别用P[i]和W[i]存储第i种物品的价值和重量。 
求解怎么装物品可使背包里物品总价值最大。 
输入 
第一行2个数n和c,表示共有n种物品,背包总容量为c  
接下来n行,每行3个数,分别表示第i种物品的重量,价值和最大数量 
输出 
一个整数,表示背包里物品最大总价值 
 
样例输入 
5 1000 
80 20 4 
40 50 9 
30 50 7 
40 30 6 
20 20 1 
样例输出 
1040 
 
算法思想: 
算法1:二维数组+朴素的穷举思想 
利用函数式B[i][j] = max(B[i-1][j-K*W[i]]+K*P[i]), 0<=K<=N[i]且0<=K*P[i]<=j,对k进行穷举,找出最大值。 
该算法与完全背包问题的朴素算法相似,只是多了一个0<=K<=N[i]的条件。 
 
算法2:一维数组+朴素的穷举思想  
该算法仍然使用穷举思想,但是用一维数组F[j]代替了二维数组B[i][j],也就是未记录行坐标,只记录了列坐标,  
与0-1背包问题类似,注意到第i行第j列的元素,由第i-1行的元素决定,且列坐标j大的元素由j小的元素决定, 
故在同一行中,必须先求出列坐标较大的元素,再求列坐标小的元素,即在内层循环中,应该让循环变量j的值从大到小递减。 
 
算法3:2个一维数组+对第i种物品进行N[i]次选择 
算法1和算法2都采用了朴素的穷举思想,在给定i个物品的情况下,对k进行穷举,找出最大值。 
有一个更为巧妙的思想可以减少部分条件判断语句,那就是把多重背包问题当做多个0-1背包问题来处理, 
即对第i个物品执行N[i]次选择。这样循环语句for (int k=0; k<N[i]; k++)只需要表示执行N[i]次选择即可,思路更清晰了。
与单纯0-1背包不同的是,对物品i进行第k次选择时,背包中可能已经装载了第i个物品了,
所以不能用B[i][j]来表示给定i个物品容量为j时的最大价值,但是可以用两个一维数组来表示,
其中cur[j]表示给定i个物品的情况下,背包容量为j时,对物品进行第k次选择时所能获得的最优解, 
pre[j]表示给定i个物品的情况下,背包容量为j时,对物品进行第k-1次选择时所能获得的最优解,
值得注意的是pre[j]并不表示给定i-1个物品的情形,但是当j<W[i]时,pre[j]的值与给定i-1个物品的情形一致,
所以即使N[i]==1(0-1背包),算法也不会出错。 
 
算法4:1个一维数组+对第i种物品进行N[i]次选择 
与0-1背包或完全背包算法一样,多重背包问题也可以用1个一维数组来代替2个一维数组, 
注意到cur[]和pre[]分别代表当前行和上一行,且cur[j]只与pre[j]和pre[j-W[i]]有关,
我们可以逆序扫描j,这样只需要一个数组F[]就能代替cur[]和pre[]了。
此时代码的结构与0-1背包基本相同,只是在中间插入了一个循环语句for (int k=0; k<N[i]; k++),
表示对第i种物品进行N[i]次0-1选择,实在是妙不可言! 
由此可见,0-1背包问题是多重背包问题的一个特例(N[i]=1),因此他们的代码结构必然是相同的, 
如果多重背包问题的第2层循环for (int k=0; k<N[i]; k++)只执行一次的话,就变成了一个纯粹的0-1背包问题。  
 
算法5:二进制优化+转化为0-1背包问题  
进行二进制优化,把第i种物品拆成重量为W[i]*2^k价值P[i]*2^k的物品,其中满足1+2+...+2^k = N[i]。 
例如,对N[6]={0,4,9,7,6,1}分别进行拆分,有4=1+2+1, 9=1+2+4+2, 7=1+2+4, 6=1+2+3, 1=1.  
此算法需要在输入数据时就对物品进行拆分,相当于增加了新的物品,这样就可以转换为0-1背包问题。  
*/  
#include<iostream>  
#include<cmath>  
  
using namespace std;  
  
const int MAXC = 6000; //背包最大容量   
const int MAXN = 2000; //物品的个数  
int W[MAXN+1];//物品的重量   
int P[MAXN+1];//物品的价值   
int N[MAXN+1];//物品的最大数量   
int F1[MAXC+1]; //记录装入容量为c的背包的最大价值  
int B1[MAXN+1][MAXC+1]; //备忘录,记录给定n个物品装入容量为c的背包的最大价值  
int pre[MAXC+1]; //pre[j]相当于B1[i-1][j] 
int cur[MAXC+1]; //cur[j]相当于B1[i][j]  
int F2[MAXC+1]; //记录装入容量为c的背包的最大价值  
int B2[MAXN+1][MAXC+1]; //备忘录,记录给定n个物品装入容量为c的背包的最大价值   
int F3[MAXC+1]; //记录装入容量为c的背包的最大价值  
  
int MultiPack_1(int n, int c);//多重背包问题:二维数组+朴素的穷举思想   
int MultiPack_2(int n, int c);//多重背包问题:一维数组+朴素的穷举思想   
int MultiPack_3(int n, int c);//多重背包问题:2个一维数组记录最优解  
int MultiPack_4(int n, int c);//多重背包问题:一维数组+对第i种物品进行N[i]次0-1选择   
int ZeroOnePack(int n, int c);//0-1背包问题:一维数组记录最优解  
  
int main()   
{  
    int n, c;  
    cin >> n >> c;  
      
    for (int i=1; i<=n; i++)//不计下标为0的元素   
    {  
       cin >> W[i] >> P[i] >> N[i];  
    }  
    
  cout << MultiPack_1(n, c) << endl;  
  cout << MultiPack_2(n, c) << endl;  
  cout << MultiPack_3(n, c) << endl;  
  cout << MultiPack_4(n, c) << endl;  
   //   
//    //进行二进制优化,把第i种物品拆成重量为W[i]*2^k价值P[i]*2^k的物品,其中满足1+2+...+2^k = N[i]。   
//    //需要在输入数据时就对物品进行拆分,相当于增加了新的物品,这样就可以转换为0-1背包问题   
//    int len = 0;//累计拆分后物品的总数量,因为第i种物品可以选多个,相当于增加了物品的种类,每种物品只有0-1选择   
//    for (int i=0; i<n; i++)    
//    {  
//        int w, p, s, t=1;  
//        cin >> w >> p >> s;  
//        //把s以2的指数幂分堆:1,2,4,...,2^(k-1), s-2^k+1  
//        while (s > t)//注意不能写成while (s >= t),否则当s=2^k-1时,会拆分出重量和价值都为0的新物品,虽然不影响最终结果,但效率变低   
//        {  
//            W[++len] = w * t;  
//            P[len] = p * t;  
//            s -= t; //注意s在变小   
//            t *= 2; //t是2的指数幂   
//        }  
//        W[++len] = w * s;  
//        P[len] = p * s;  
//    }   
//    //获得新的物品的种类数据后,直接当做0-1背包问题处理   
//    cout << ZeroOnePack(len, c) << endl;  
      
    return 0;  
}  
  
int MultiPack_1(int n, int c)//多重背包问题:二维数组+朴素的穷举思想   
{  
    int bestP;  
      
    for (int i=1; i<=n; i++)  
    {  
        for (int j=1; j<=c; j++) //j递增或递减均可   
        {  
            //朴素的穷举思想,计算第i个物品装k个时,获得多少价值,存储最大价值到B[i][j]   
            bestP = 0;  
            for (int k=0; k<=N[i] && k*W[i]<=j; k++)  
            {  
                if (bestP < B1[i-1][j-k*W[i]] + k*P[i])  
                    bestP = B1[i-1][j-k*W[i]] + k*P[i];  
            }  
            B1[i][j] = bestP;  
        }  
    }  
      
    return B1[n][c];  
}  
  
int MultiPack_2(int n, int c)//多重背包问题:一维数组+朴素的穷举思想   
{  
    int bestP;  
      
    for (int i=1; i<=n; i++)  
    {  
        for (int j=c; j>=W[i]; j--) //类似于0-1背包问题,j只能递减  
        {  
            //朴素的穷举思想,计算第i个物品装k个时,获得多少价值,存储最大价值到F[j]   
            bestP = 0;  
            for (int k=0; k<=N[i] && k*W[i]<=j; k++)  
            {  
                if (bestP < F1[j-k*W[i]] + k*P[i])  
                    bestP = F1[j-k*W[i]] + k*P[i];  
            }  
            F1[j] = bestP;  
        }  
    }  
      
    return F1[c];  
}  
 // 
//int MultiPack_3(int n, int c)//因为B2[i][j]不能及时迭代,故算法错误 
//{  
//	int maxNum = 0; //记录对第i种物品进行0-1选择的最多次数 
//      
//    for (int i=1; i<=n; i++)  
//    {  
//		maxNum = c / W[i]; //即使是完全背包,也最多选择maxNum次 
//        if (N[i] > 0 && maxNum > N[i]) //非完全背包  
//        {   
//            maxNum = N[i];
//        }  
//        for (int k=maxNum; k>0; k--)//对第i种物品进行maxNum次选择 
//        {  
//            for (int j=1; j<=c; j++)      
//            {  
//                if (j < W[i]) //容量不够,则和给定i-1个物品装入容量为j的背包的结果一致   
//                {  
//                    B2[i][j] = B2[i-1][j];  
//                }  
//                else //B2[i][j-W[i]]表示给定i个物品装入容量为j-W[i]的背包,质量为W[i]的物品可能已经装了多个   
//                {  
//                    B2[i][j] = max(B2[i-1][j], B2[i][j-W[i]] + P[i]);  
//                }     
//            }   
//        }   
//    }  
//      
//    return B2[n][c];  
//}  

int MultiPack_3(int n, int c)//多重背包问题:2个一维数组记录最优解  
{  
	//cur[j]表示给定i个物品的情况下,背包容量为j时,对物品进行第k次选择时所能获得的最优解 
    //pre[j]表示给定i个物品的情况下,背包容量为j时,对物品进行第k-1次选择时所能获得的最优解  
    for (int i=1; i<=n; i++)  
    {  
        for (int k=0; k<N[i]; k++)//对第i种物品进行k次选择  
        {
			for (int j=1; j<=c; j++)
			{
				if (j < W[i] || pre[j] > pre[j-W[i]] + P[i])
					cur[j] = pre[j]; 
				else
					cur[j] = pre[j-W[i]] + P[i];
			}
			for (int j=1; j<=c; j++)//更新后的pre[j]有可能是选择了第i种物品 
			{
				pre[j] = cur[j]; 
			}
        }  
    }  
      
    return pre[c];  
}  
  
int MultiPack_4(int n, int c)//多重背包问题:一维数组+对第i种物品进行N[i]次0-1选择   
{  
    for (int i=1; i<=n; i++)  
    {  
        for (int k=0; k<N[i]; k++)//对第i种物品进行N[i]次0-1选择   
        {//须先求出列坐标j较大的元素,故让循环变量j的值从大到小递减  
            for (int j=c; j>=W[i]; j--)  
            {//当(j < W[i] || F2[j] > F2[j-W[i]] + P[i])时,F2[j]的值不变  
                if (F2[j] < F2[j-W[i]] + P[i])  
                    F2[j] = F2[j-W[i]] + P[i];  
            }  
        }  
    }  
      
    return F2[c];  
}  
  
int ZeroOnePack(int n, int c)//0-1背包问题:一维数组记录最优解  
{  
    for (int i=1; i<=n; i++)  
    {//须先求出列坐标j较大的元素,故让循环变量j的值从大到小递减  
        for (int j=c; j>=W[i]; j--)  
        {//当(j < W[i] || F3[j] > F3[j-W[i]] + P[i])时,F3[j]的值不变  
            if (F3[j] < F3[j-W[i]] + P[i])  
                F3[j] = F3[j-W[i]] + P[i];  
        }  
    }  
      
    return F3[c];  
}  

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
多重背包问题是指在一定的背包容量下,有多个物品的重量和价值,每个物品有一定的数量限制,要求在放入背包时,使得背包中物品的总价值最大化。动态规划是解决多重背包问题的一种常见方法。 其中,引用提供了一种将多重背包问题转化为01背包问题的方法。这种方法将每个多重背包问题的物品按照其数量进行拆分,使其变成多个01背包问题。然后使用动态规划的思想,依次求解每个01背包问题,最终得到多重背包问题的最优解。 引用提供了一种对完全背包问题的暴力解法做简单修改的方法。该方法同样使用动态规划的思想,对每个物品进行遍历,并在背包承重为j的前提下,求解每个物品能放入的最大数量。然后根据物品的重量和价值进行计算,得到多重背包问题的最优解。 这两种方法都可以用来解决多重背包问题,并根据实际情况选择合适的方法进行求解。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [动态规划(五):多重背包问题](https://blog.csdn.net/qq_42138662/article/details/118434151)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [动态规划:多重背包问题](https://blog.csdn.net/qq_42174306/article/details/124345411)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [动态规划4:多重背包](https://blog.csdn.net/qq_40772692/article/details/81435230)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值