混合背包问题

/* 
    Name: 混合背包问题  
    Copyright:  
    Author:  巧若拙 
    Date: 07-06-18 09:33 
    Description:  
混合背包问题 :在n种物品中选取若干件放在容量为c的背包里,分别用P[i]和W[i]存储第i种物品的价值和重量。 
有的物品只可以取一次(01背包),有的物品可以取无限次(完全背包),有的物品可以取的次数有一个上限(多重背包) 
求解怎么装物品可使背包里物品总价值最大。 
         
输入 
第一行2个数n和c,表示共有n种物品,背包总容量为c  
接下来n行,每行3个数Wi,Pi和Ni,分别表示第i种物品的重量,价值和最大数量(若Ni=0,则表示此物品可取无限次)  
输出 
一个整数,表示背包里物品最大总价值 
 
样例输入 
3 10 
2 1 0 
3 3 1 
4 5 4 
样例输出 
11   
 
算法分析: 
考虑到0-1背包是多重背包问题的一个特例,所以混合背包问题可以分为完全背包和多重背包两种情况分析。 
只需要判断Ni的值,然后分别使用完全背包和多重背包的算法来写代码就行了。 
我们可以用2个一维数组记录最优解,也可以进一步优化,使用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 pre[MAXC+1]; //pre[j]相当于B[i-1][j] 
int cur[MAXC+1]; //cur[j]相当于B[i][j]
int F[MAXC+1]; //记录装入容量为c的背包的最大价值  
int F2[MAXC+1]; //记录装入容量为c的背包的最大价值  
int F3[MAXC+1]; //记录装入容量为c的背包的最大价值  
  
int MixPack_1(int n, int c);//混合背包问题:2个一维数组记录最优解  
int MixPack_2(int n, int c);//混合背包问题:一维数组记录最优解  
int MixPack_3(int n, int c);//混合背包问题:全部当作多重背包来解,代码结构更简洁,但是完全背包的效率降低 
int MixPack_4(int n, int c);//混合背包问题:全部当作多重背包来解,使用了朴素的穷举算法 
  
int main()   
{  
    int n, c;  
    cin >> n >> c;  
      
    for (int i=1; i<=n; i++)//不计下标为0的元素   
    {  
        cin >> W[i] >> P[i] >> N[i];  
    }  
      
    cout << MixPack_1(n, c) << endl;  
    cout << MixPack_2(n, c) << endl;  
    cout << MixPack_3(n, c) << endl; 
    cout << MixPack_4(n, c) << endl; 
      
    return 0;  
}  

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

int MixPack_3(int n, int c)//混合背包问题:全部当作多重背包来解,代码结构更简洁,但是完全背包的效率降低 
{  
	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次0-1选择   
        {//须先求出列坐标j较大的元素,故让循环变量j的值从大到小递减  
            for (int j=c; j>=W[i]; j--)  
            {//当(j < W[i] || F[j] > F[j-W[i]] + P[i])时,F[j]的值不变  
                if (F2[j] < F2[j-W[i]] + P[i])  
                    F2[j] = F2[j-W[i]] + P[i];  
            }  
        }  
    }  
      
    return F2[c];  
}  

int MixPack_4(int n, int c)//混合背包问题:全部当作多重背包来解,使用了朴素的穷举算法    
{  
    int s, bestP;  
    int maxNum = 0; //记录对第i种物品选择的最多次数     
      
    for (int i=1; i<=n; i++)  
    {  
        for (int j=c; j>=W[i]; j--) //类似于0-1背包问题,j只能递减  
        {  
            //朴素的穷举思想,计算第i个物品装k个时,获得多少价值,存储最大价值到B[i][j]   
            bestP = s = 0;  
            maxNum = c / W[i]; //即使是完全背包,也最多选择maxNum个   
            if (N[i] > 0 && maxNum > N[i]) //非完全背包      
            {       
                maxNum = N[i];    
            }    
            for (int k=maxNum; k>0; k--)  
            {  
                if (k*W[i] <= j)  
                {  
                    s = F3[j-k*W[i]] + k*P[i];   
                    if (s > bestP)  
                        bestP = s;  
                }  
            }  
            if (F3[j] < bestP) //有更好的解才更新   
                F3[j] = bestP;  
        }  
    }  
      
    return F3[c];  
}  

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值