畅谈分组背包问题

分组背包问题是背包问题的一种变体,它在一组物品中进行选择,每个物品属于某个特定的组。问题的描述通常是这样的:给定若干组物品,每组物品都有自己的重量、价值以及数量限制。目标是选择若干组物品放入背包中,使得背包中物品的总价值最大。

这里用acwing上的例题:9. 分组背包问题 - AcWing题库


朴素解法

这里朴素解法利用的二维数组f[i][j]来进行状态转移,枚举物品组数,枚举体积,枚举对于每组物品的决策,选还是不选,以得到最优解。这里朴素解法不再详细介绍,只放一个代码段,因为后面还可以优化一下。

其实看起来跟多重背包的朴素解法差不多,有什么不同的,下面放上了两段代码,比较看一下。不同在了就是在状态转移上了,多重背包呢可以选多个所以用k来控制,分组背包呢,例题中一组背包中最多只允许选一个,就是对于一组背包可以不选可以选一个。每一个物品都是不同的、有个性的,没有完全相同的,第三个for循环就是在枚举每一组具体的物品。


一维优化解法

对照朴素解法,多重背包呢时间复杂度那个for循环k的可以优化掉,空间复杂度也可以优化成一维的,但是时间复杂度优化是有条件的,多重背包呢可以组合进行二进制优化,也可以分类进行单调队列优化。分组背包呢,第i组物品的体积价值都是各不相同的,毫无规律可言,何以优化,但是在空间复杂度上可以优化成一维,f[i][j]是在f[i-1][j]转移过来的,就是在前一组物品决策完转移过来的,只是用了上一行的数据,有点类似01背包的一维优化。

#include<iostream>
using namespace std;
int dp[1005],v[1005],w[1005];//dp[i]表示背包容量为i时所获得最大价值
int N,V;
int main(){
	cin>>N>>V;
	int p;//p表示第p组物品
	for(int i=1;i<=N;i++){
		cin>>p;
		for(int j=1;j<=p;j++){
			cin>>v[j]>>w[j];
		}
        //因为dp[j]会先于dp[j-v[k]]更新,所以dp[j-v[k]]的值等价于dp[i-1][j-v[k]]
		for(int j=V;j>=1;j--){//逆序枚举背包容量
			for(int k=1;k<=p;k++){
				if(j>=v[k]){
					dp[j]=max(dp[j],dp[j-v[k]]+w[k]);
				}
			}
		}
	}
	cout<<dp[V]<<endl;
	return 0;
}

这里注意枚举背包容量和枚举k顺序不可颠倒,不然dp[j-v[k]]的值就不等价于dp[i-1][j-v[k]]了。


变式题型

小编做过其他类型的题,但都是分组背包的变式。有的题呢,一组物品给你限定了选多少个,比如选2个3个,例题中最多选一个,这样的话就比较麻烦了,既要确定选了哪一组物品又要达到选几个的要求,还是再次基础上,利用贪心,在每一组背包选几个的要求基础上,使得每一组背包都是最优的就可以(01背包)。用背包容量去枚举每一组背包,再去加一个if判断是否达到选几个的要求。有的呢还会物品会乱序输入,让你自己去分组好,再去进行选择。下面放一个我写过的题的代码,组数打乱自己找的(当然题中样例每打乱)。

#include<iostream>
#include<vector>
using namespace std;
int w[31],c[31];
int dp[201];
int v,n,p,t;
int main(){
	cin>>v>>n>>t;//v容量n物品t组数
	vector<vector<int>> group(t+1);
	for(int i=1;i<=n;i++){
		cin>>w[i]>>c[i]>>p;//p属于第几组
		group[p].push_back(i);//把下标往里抛就行
	}
	for(int i=1;i<=t;i++){//与一维优化类似
		for(int j=v;j>=0;j--){
			for(int k=0;k<group[i].size();k++){
				int index=group[i][k];
				if(j>=w[index]){
					dp[j]=max(dp[j],dp[j-w[index]]+c[index]);
				}
			}
		}
	}
	cout<<dp[v]<<endl;
	return 0;
}
/*
10 6 3
2 1 1
3 3 1
4 8 2
6 9 2
2 8 3
3 9 3
*/
//20

分组背包呢,听起来就是分组分组……,无非还是01背包的变形,背包问题呢,变形非常多,但是万变不离其宗,方法会了,其他变式也都迎刃而解。小编水平有限,能说的都在文章展现了,有错误的地方请大家指出,大家有疑问的地方随时可以私信我,看到必答。下篇更新有依赖的背包。

  • 50
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

摆烂小白敲代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值