1.概念
分组背包:
分组背包就是有n组物品,每组物品中只可以选择一个物品。
每个物品都有体积和价值,求总体积不超过m的情况下的价值最大值。
那么我们就可以通过概念来得到状态转移方程:
dp[ j ]=max(dp[ j ],dp[ j -w[ i ][ k ] ]+v[ i ][ k ];
如果现在想不到也不要着急,马上就到了进入本文的重点
2.分组背包的思想以及相关的代码实现
分组背包是有三层循环,
第一层循环是遍历每一组,
第二层循环遍历的是容量,
第三层循环遍历的是组内的每一个成员
相关代码:
//假设有t组,最大容量为m,s数组中存的是每组有多少成员
for(int i=1;i<=t;i++)//先遍历组
{
for(int j=m;j>=0;j--)//容量
{
for(int k=1;k<=s[i];k++)//小组中的物品
{
if(j-w[i][k]>=0)
{
dp[j]=max(dp[j],dp[j-w[i][k]]+v[i][k]);
}
}
}
}
3.相关例题
第一题:通天之分组背包
题解:就是最基础的分组背包问题,无需多言
AC代码:
#include<bits/stdc++.h>
using namespace std;
int m,n;
int w[105][1005];//代表第i组的第j个物品的重量
int v[105][1005]; //代表第i组的第j个物品的价值
int s[105];//统计每组中有多少数
int dp[1005];//dp数组容量为j时,所能装最大价值
int t;
int main()
{
scanf("%d%d",&m,&n);
for(int i=1;i<=n;i++)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
s[c]++;
t=max(t,c);
w[c][s[c]]=a;
v[c][s[c]]=b;
}
for(int i=1;i<=t;i++)//先遍历组
{
for(int j=m;j>=0;j--)//容量
{
for(int k=1;k<=s[i];k++)//小组中的物品
{
if(j-w[i][k]>=0)
{
dp[j]=max(dp[j],dp[j-w[i][k]]+v[i][k]);
}
}
}
}
printf("%d",dp[m]);
return 0;
}
第二题:排兵布阵
题解:
我们可以把每个城堡看做一组物品。每个人的兵看成体积,把城堡编号看成价值。
但是难点分组背包是每一个组只能选一个,这里是可以打多个对手。
其实可以发现,假设第i个对手派出的兵<第i+1个对手派出的兵,那么如果我们派出的兵可以打赢i+1个对手派出的兵,那么也肯定能打赢第i个对手。
所以,我们可以把每个城堡分别的对手派出的兵数进行排序。状态转移的时候,把分组背包的枚举哪一个物品改成从小到打枚举哪几个物品,这样就可以转化为分组背包。
请看AC代码:
#include<bits/stdc++.h>
using namespace std;
int s,n,m;
int dp[20005];
int a[105][105];
int main()
{
scanf("%d%d%d",&s,&n,&m);
for(int i=1;i<=s;i++)
{
for(int j=1;j<=n;j++)
{
scanf("%d",&a[j][i]);
}
}for(int i=1;i<=n;i++)
sort(a[i]+1,a[i]+s+1);
for(int i=1;i<=n;i++)
{
for(int j=m;j>=0;j--)
{
for(int k=1;k<=s;k++)
{
if(j-2*a[i][k]>0)
{
dp[j]=max(dp[j],dp[j-2*a[i][k]-1]+i*k);
}
}
}
}
printf("%d",dp[m]);
return 0;
}