首页 > 其他分享 >背包九讲——分组背包问题

背包九讲——分组背包问题

时间:2024-10-30 17:21:37浏览次数:4  
标签:背包 九讲 int 分组 解法 物品 dp

目录

分组背包问题

问题定义

解题算法

问题解法

朴素解法:

一维优化解法

变式题型


背包问题第六讲——分组背包问题

背包问题是一类经典的组合优化问题,通常涉及在限定容量的背包中选择物品,以最大化某种价值或利益。问题的一般描述是:有一个背包,其容量为C;有一组物品,每个物品有重量w和价值v。目标是选择一些物品放入背包,使得它们的总重量不超过背包容量,同时总价值最大。
分组背包问题是背包问题的变体,它的一般描述为对物品进行分组,对每一组内的物品规定只能选择k个。

分组背包问题

分组背包问题(Grouped Knapsack Problem)是组合优化中的一个问题,它是经典的背包问题的变种。在分组背包问题中,有多个物品组,每组中的物品不可分割,并且每组中的物品数量至少有一个。目标是在不超过背包容量的前提下,选择物品的组合,使得总价值最大。

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

问题定义

  • 物品:有 n 组物品,每组有若干个不可分割的物品。
  • 背包容量:背包可以承载的最大重量为 W。
  • 价值:每组物品有一个价值。
  • 重量:每组物品有一个重量。
  • 目标:选择一些组的物品,使得总重量不超过 W,且总价值最大。

解题算法

  1. 动态规划:这是解决分组背包问题最常用的方法。

    状态定义:定义f[i[j]表示考虑前 i 组物品,当前背包容量为 j 时的最大价值。
    • 状态转移方程
      • 如果不选第 i 组物品:f[i][j]=f[i-1][j]
      • 如果选第 i 组物品(前提是 j 至少可以装下第 i 组物品):f[i][j] = max(f[i][j], dp[i-1][j-w[i]] + v[i])
    • 初始化:f[0][j]=0,因为没有物品时价值为0。
    • 遍历顺序:先遍历物品组,再遍历背包容量。
  2. 贪心算法:在某些情况下,如果物品的价值和重量满足某种比例关系,可以使用贪心算法。

  3. 回溯法:尽管效率较低,但可以用来验证问题的解。


问题解法

朴素解法:

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

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

//多重背包朴素解法
for(int i=1;i<=n;i++){//物品
	for(int j=1;j<=V;j++){//体积
		for(int k=0;k<=s[i];k++){//决策
			if(j>=k*v[i]){
				f[i][j]=max(f[i][j],f[i-1][j-k*v[i]])+k*w[i];
			}
		}
	}
}
cout<<f[n][V]<<endl;

//分组背包朴素解法
for(int i=1;i<=n;i++){//第几组物品
	for(int j=1;j<=V;j++){//体积
		for(int k=0;k<=s[i];k++){//决策
			if(j>=v[i][k]){
				f[i][j]=max(f[i][j],f[i-1][j-v[i][k]]+w[i][k]);
			}
		}
	}
}
cout<<f[n][V]<<endl;

一维优化解法

对照朴素解法,多重背包呢时间复杂度那个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

上篇文章:背包九讲——二维费用背包问题-CSDN博客

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

标签:背包,九讲,int,分组,解法,物品,dp
From: https://blog.csdn.net/m0_73633807/article/details/142719724

相关文章

  • 【深基12.例1】部分背包问题——仔细检查数据类型!
    题目描述阿里巴巴走进了装满宝藏的藏宝洞。藏宝洞里面有\(N(N\le100)\)堆金币,第\(i\)堆金币的总重量和总价值分别是\(m_i,v_i(1\lem_i,v_i\le100)\)。阿里巴巴有一个承重量为\(T(T\le1000)\)的背包,但并不一定有办法将全部的金币都装进去。他想装走尽可能多价值的金......
  • 01背包问题(经典dp题解)
    有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。第 ii 件物品的体积是 vi,价值是 wi。求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出最大价值。输入格式第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。接下来......
  • 13. 分组数据
    1.数据分组分组允许把数据分为多个逻辑组,以便能对每个组进行聚集计算2.创建分组分组是在SELECT语句的GROUPBY子句中建立的。比如:selectvend_id,count(*)asnum_prodsfromproductsgroupbyvend_id;输出如下:上面的SELECT语句指定了两个列,vend_id包含产品供应商的......
  • 机器学习中的算法—背包问题
    原文链接:机器学习中的算法—背包问题–每天进步一点点背包问题是一种资源分配算法,是一种非常典型的问题,是对资源分配最大化的体现。倘若有n件物品,其中每件物品都有属于自己的质量和价值,现在仅有一个背包,但是背包最大的承载量为w,因此需要试图在这个背包里装一些物品,使得物品的......
  • 背包问题-分支限界法求解
    此为课题组所指导本科生和低年级硕士生学习组合优化问题汇报所用教材:北京大学屈婉玲教授《算法设计与分析》课程资料:https://www.icourse163.org/course/PKU-1002525003承诺不用于任何商业用途,仅用于学术交流和分享更多内容请关注许志伟课题组官方中文主页:https://JaywayXu.g......
  • ASP.Net Core 8 Web API整合Swagger UI并进行模块分组吃屎般瞬间记录
    一、开发环境开发工具:VisualStudio2022工程模板:ASP.NetCore8WebAPI工程(官方标准的).Net环境:.NetCore8.0NuGet依赖:Swashbuckle.AspNetCore6.9.0(UI用的默认的UI界面,可以自由选择其他的UI界面)二、基本概述参考了网上很多大佬的帖子,实现基本就两种:1、用自定义At......
  • 数字分组求偶数和
    问题描述小M面对一组从1到9的数字,这些数字被分成多个小组,并从每个小组中选择一个数字组成一个新的数。目标是使得这个新数的各位数字之和为偶数。任务是计算出有多少种不同的分组和选择方法可以达到这一目标。numbers:一个由多个整数字符串组成的列表,每个字符串可以视为......
  • CodeForces - 788C - 完全背包
    题目表示(x1*a[1]+x2*a[2]+...+xk*a[k])/((x1+x2+...+xk)*1000)=n/1000,可以推出(x1*a[1]+x2*a[2]+...+xk*a[k])=n*(x1+x2+...+xk),进而得出(x1*(a[1]-n)+x2*(a[2]-n)+...+xk*(a[k]-n))=0,这样处理之后就和我之前......
  • DAY42 ||完全背包理论 | 518. 零钱兑换 II | 377. 组合总和 Ⅳ|70. 爬楼梯 (进阶)
    完全背包理论什么是完全背包:有N件物品和一个最多能背重量为W的背包。第i件物品的重量是weight[i],得到的价值是value[i]。每件物品都有无限个(也就是可以放入背包多次),求解将哪些物品装入背包里物品价值总和最大。完全背包和01背包问题唯一不同的地方就是,每种物品有无限件。不......
  • 【C++-NOIP篇-4】 [NOIP2007 普及组] 纪念品分组
    文章目录[NOIP2007普及组]纪念品分组题目背景题目描述输入格式输出格式样例#1样例输入#1样例输出#1提示题目思路完整Code[NOIP2007普及组]纪念品分组题目背景NOIP2007普及组T2题目描述元旦快到了,校学生会让乐乐负责新年晚会的纪念品发放工作。为使得参......