其实一开始并不是用分组背包AC的(当是还没学),而是01背包加分类讨论过的,但写的很复杂,觉得应该有更简单的算法,查了才知道可以用分组背包的。
分组就是将主件与附件看成是同一组内的,主件与其他主件为不同组。同一组内有4种方案,取主件,取主件+附件1,取主件+附件2,取主件+附件3,这几个方案都是互斥的,符合分组背包的特点。
分组背包的AC代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<iostream>
using namespace std;
typedef long long ll;
const int maxn=32000+5;
vector<ll> v[61],val[61];
ll dp[maxn];
bool is_main[61]={0};
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++)
{
ll a,b,c;
cin>>a>>b>>c;
if(!c)
{
v[i].push_back(a);
val[i].push_back(a*b);
is_main[i]=1;
}
else
{
int size=v[c].size();
for(int i=0;i<size;i++)
{
ll temp=v[c][i];
v[c].push_back(a+temp);
}
for(int i=0;i<size;i++)
{
ll temp=val[c][i];
val[c].push_back(a*b+temp);
}
}
}
memset(dp,0,sizeof(dp));
for(int i=1;i<=m;i++) //分组背包标准模板
{
if(!is_main[i]) continue;
for(int j=n;j>=0;j--)
{
for(int k=0;k<v[i].size();k++)
{
if(j>=v[i][k])
dp[j]=max(dp[j],dp[j-v[i][k]]+val[i][k]);
}
}
}
ll ans=0;
cout<<dp[n]<<endl;
return 0;
}
分类讨论需要设置结构体完成DP,在下面的代码中会有解释。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
using namespace std;
typedef long long ll;
const int maxn=32000+5;
struct subject
{
ll sum; //记录最大收益
bool check[61]; //check[i]=1表示i物品有购买,0表示没购买
subject()
{
sum=0;
memset(check,0,sizeof(check));
}
}dp[maxn];
ll v[61],val[61];
int fa[61];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++)
{
ll a,b,c;
cin>>a>>b>>c;
v[i]=a; val[i]=a*b; fa[i]=c;
}
for(int i=1;i<=m;i++)
for(int j=n;j>=v[i];j--)
{
if(!fa[i]) //如果是主件,就正常的01背包就好
{
if(dp[j].sum<dp[j-v[i]].sum+val[i])
{
dp[j]=dp[j-v[i]];
dp[j].sum+=val[i];
dp[j].check[i]=1; //i被购买了
}
}
else //如果是附件
{
if(dp[j-v[i]].check[fa[i]]) //如果j-v[i]的方案中,有购买fa[i]
{
if(dp[j].sum<dp[j-v[i]].sum+val[i])
{
dp[j]=dp[j-v[i]];
dp[j].sum+=val[i];
dp[j].check[i]=1;
}
}
else if(j>=v[i]+v[fa[i]]) //如果j-v[i]的方案中没有fa[i],就把i与fa[i]看成一个整体,
//判断是否购买这个整体会不会更优,前提是钱够(j>=v[i]+v[fa[i]])
{
if(dp[j].sum<dp[j-v[i]-v[fa[i]]].sum+val[i]+val[fa[i]])
{
dp[j]=dp[j-v[i]-v[fa[i]]];
dp[j].sum+=val[i]+val[fa[i]];
dp[j].check[i]=dp[j].check[fa[i]]=1;
}
}
}
}
cout<<dp[n].sum<<endl;
return 0;