**终于从数据结构的魔圈中走出来啦!!!,今天我终于要写一篇关于 动态规划的题目啦,哈哈哈! **
没看过题目的请戳这里☞
分组背包
先不说什么,直接上代码(代码中有变量解释):
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
struct sd{//结构体
int weight ,value;//重量,价值
}subject;//物品特性
bool judger[202];//判断函数,后面会讲
int M[202];//背包常用数组
vector<sd> group[15];//这里指的是所分的组数
int main()
{
int contain,count,team;//背包重量,物品数量,组数
scanf("%d%d%d",&contain,&count,&team);
int number;//组号
for(int i=1;i<=count;++i)
{
scanf("%d%d%d",&subject.weight,&subject.value,&number);
group[number].push_back(subject);//把同样的组号的物品放在一起!
}
int limit=0;
for(int i=1;i<=team;++i)
{
for(int j=limit;j>=0;--j)
{
for(int k=group[i].size()-1;k>=0;--k)
{
if((j==0||M[j]!=0)&&j+group[i][k].weight<=contain)
{
if(!judger[j]&&M[j+group[i][k].weight]<M[j]+group[i][k].value)
{
M[j+group[i][k].weight]=M[j]+group[i][k].value;
judger[j+group[i][k].weight]=true;
}
if(j+group[i][k].weight>limit)
limit=j+group[i][k].weight;
}
}
}
memset(judger,false,sizeof(judger));
}
int ans=-1;
for(int i=0;i<=contain;++i)
{
if(ans<M[i])
ans=M[i];
}
printf("%d",ans);
return 0;
}
其实认真学过01背包的同学会发现这两个代码其实是非常相似的
下面来对比一波
- 01包代码和分组包代码都是用到了几个for循环,而分组包只是比01包多了一个for循环,用来循环组内的数据(见打了标记的那个for循环)
- 分组包所申请的变量只比01包多了一个judger(变量)
- 然后就没有什么明显区别啦!
下面来一波01包的代码:
#include<cstdio>
#include<cstring>
using namespace std;
int bag[305];
int m,n;
int main()
{
scanf("%d%d",&m,&n);
int a,b;
int limit=0;
for(int i=1;i<=n;++i)
{
scanf("%d%d",&a,&b);
for(int j=limit;j>=0;--j)
{
if(bag[j]!=0||(j==0))
{
if(bag[j+a]<bag[j]+b&&j+a<=m)
{
bag[j+a]=bag[j]+b;
}
if(limit<j+a)
limit=j+a;
}
}
}
int ans=-1;
for(int i=0;i<=m;++i)
{
if(bag[i]>ans)
ans=bag[i];
}
printf("%d",ans);
return 0;
}
judger数组
这里就是分组背包的精髓,judger变量是用来记录当前组内可以扔到背包中的当前点是否已经被组内的其他物品扔到了!如果没有,那就把这个物品扔进去,把扔过他以后到达的那个点的judger记为true(即下一次组内物品往里面背包中扔的时候,不能往刚才扔到的那个点上面扔)(因为题目上说,两个同组的物品不能碰在一起,否则会出事情!嘿嘿嘿!!!),所以说,每次当遍历完一个组的时候,我们就需要把judger数组memset一下,以便于用于下一个数组。
P.s:好好看代码并理解加粗的字,相信你可以想通这个问题!!!
更多背包问题详解请见
背包, 背包