分组背包问题
问题重述
有 N 组物品和一个容量是 V 的背包。每组物品有若干个,同一组内的物品最多只能选一个。每件物品的体积是 vij,价值是 wij,其中 i 是组号,j 是组内编号。求解将哪些物品装入背包,可使物品总体积不超过背包容量,且总价值最大。输出最大价值。
输入格式
第一行有两个整数 N,V,用空格隔开,分别表示物品组数和背包容量。
接下来有 N 组数据:
每组数据第一行有一个整数 Si,表示第 i 个物品组的物品数量;
每组数据接下来有 Si 行,每行有两个整数 vij,wij,用空格隔开,分别表示第 i 个物品组的第 j 个物品的体积和价值;
输出格式
输出一个整数,表示最大价值。
数据范围
0<N,V≤100
0<Si≤100
0<vij,wij≤100
思路分析:
分组背包问题其实也是由01背包演化而来,它就在01背包的基础上加入了组别的概念一个物品组至多选一个。其实分组背包和多重背包也是有相同之处的,面对一个有counts个物品的物品组我们一共有 (counts+1) 个选择即要么不选、要么选第一个、要么选第二个…… 而多重背包里对一个有counts个的物品我们也有 (counts+1) 个选择,即要么不选、要么选一个、要么选两个……所以从这个角度看多重背包可以看做分组背包的特例。即如果分组背包组内物品的体积和价值依次以最小的那个成1、2、3……倍的关系它就是多重背包了。
那么熟悉了01背包,分组背包的解法也很简单即我们从遍历物品转为遍历组别,然后再在组内收集当前组内每种物品的属性。然后从大到小遍历体积,当情况允许时(即背包体积可以放下当前物品),我们把每种可能比较一下,即比较一下:不选、选第一个、选第二个……的各种价值,保留最优解即可。当组别全都遍历完后,即可得到前N组在体积不超过V的情况下的最优解。
C++代码:
#include<iostream>
#include<vector>
using namespace std;
struct thing{//定义结构体储存物品属性包括体积和价值
int vi;
int wi;
};
int N,V;
vector<int>ans;
int main()
{
cin>>N>>V; //输入物品组数和背包的体积
ans.resize(V+10,0); //开辟合适空间的一维数组动态存储答案
for(int i=0;i<N;i++)//遍历组数
{
int s;
cin>>s; //输入当前组的物品数
vector<thing>temp; //定义一个容器去存储当前组每一个物品的属性(体积、价值)
for(int j=0;j<s;j++)//输入每一个物品的属性并存入容器
{/*因为我们每组至多只能选一个物品所以我们必须用每组的所有物品属性然后在各
个体积下依次比较选每一种物品的价值大小选最优的,所以物品属性要在不同体
积下都用所以不能现场输入了每个物品的属性了,只能现场输入每组物品的属性*/
int Vi,Wi;
cin>>Vi>>Wi;
temp.push_back({Vi,Wi});
}
for(int k=V;k>=0;k--)//物品要选只能选一次所以体积从大到小遍历
{
for(int j=0;j<s;j++)//遍历组内每一种物品比较选哪种产生的价值最大当然也有可能全不选
{
if(k>=temp[j].vi)//在体积能放入物品的情况下尝试放入物品并和当前比较
ans[k]=max(ans[k],ans[k-temp[j].vi]+temp[j].wi);//状态转移方程
}
}
}
cout<<ans[V]<<endl;//输出答案
return 0;
}