一.分组背包(每组只能选一个)
所谓分组背包,就是把物品分成n组,每组里面m个物品,从这n组中每组选一个物品,使得在背包体积是V的条件下价值最大;
思路:对于每一组由于只能选一个,所以就是决策这一组中选哪一个获得的价值最大;
for(int i=1;i<=n;i++) //枚举这是第几组 ,num[i]代表第i组物品的个数,v[i][j]表示第i组第j个物品的体积,w同理
{
for(int k=V;k>=0;k--) //枚举体积
for(int j=1;j<=num[i];j++) //枚举这一组内的第几个物品
{
if(k>=v[i][j])
dp[k]=max(dp[k],dp[k-v[i][j]]+w[i][j]) //进行决策。
}
}
二:混合背包
混合背包就是给n个物品,其中一些物品只有一个,还有一些有有限个,另一些有无限个,求取在背包体积为V的条件下所能达到的最大价值.
思路:把物品重新进行分类,有限个可以通过二进制优化为01背包,一个就是01背包,然后标记一下当前物品是要进行01背包还是完全背包就完事儿.
int cnt=0,w[SIZE],v[SIZE],mark[SIZE]; //cnt为分堆后物品的个数,v为花费,w为价值,mark标记数组,标记这个物品进行的种类
for(int i=1;i<=n;i++)
{
int val,cost,s;
cin>>val>>cost>>s;
if(s==-1) //-1为完全背包,标记为1
{
w[++cnt]=val;v[cnt]=cost;mark[cnt]=1;
}
else if(s==1) //01背包
{
w[++cnt]=val;v[cnt]=cost;mark[cnt]=0;
}
else //多重背包
{
int k=1;
while(k<s)
{
w[++cnt]=val*k;v[cnt]=cost*k;mark[cnt]=0;
s-=k;
k<<=1;
}
if(s) {w[++cnt]=val*s;v[cnt]=cost*s;mark[cnt]=0;}
}
}
//根据分组进行对应的01背包和完全背包 balabalabala
三.有依赖的背包
简单来说就是给n个物品,但这n个物品有些之间有依赖关系,比如a和b,如果要选a,就必须选择b,选择了b,却不一定选择a,在这种条件下体积V能达到的最大价值.
思路:曾经碰到的时候一看题解就觉得很复杂,于是暂时放了下,后来在学树形DP时,又看到这个问题,其实这个本质就是一个树形DP;因为这n个物品的关系网就构成了一棵树,在树上进行决策。我们假设已经建好了物品之间的关系树
//模板
void dfs(int now,int pre) //dp[i][j]代表以i为根结点,最大体积为j时能获得的最大价值
{
for(int i=head[now];~i;i=edge[i].next)
{
int v=edge[i].v;
if(v==pre) continue;
dfs(v,now); //先更新子节点的状态,再更新父节点的状态
for(int j=V-cost[now];j>=0;j--) //为什么是V-cost[now],因为我们必须要拿当前的物品才能拿这个物品下面的物品,因此要预留一个当前物品的空间来更新状态
for(int k=0;k<=j;k++) //枚举子节点的每个状态
dp[now][j]=max(dp[now][j],dp[now][j-k]+dp[v][k]);//如果我要拿子物品,那么当前就需要预留子物品所占的体积,也就是k
}
//更新dp[now][];
for(int i=V;i>=cost[now];i--) dp[now][i]=dp[now][i-cost[now]]+val[now]; //子节点对当前节点的影响都更新完了,应该更新当前节点的状态,由于于要拿字节点,所以当前节点必须拿
for(int i=0;i<cost[now];i++) dp[now][i]=0; //拿不了当前物品的,其子节点也不能拿,也就是最大价值是0;
}
用这个方法去写“金明的预算方案”的时候T了,真是杀鸡用了牛刀,因为附件只有0,1,2三种情况,就相当于多了几种情况的01背包,在每种情况中取最大就行了
#include<iostream>
#include<algorithm>
#include<vector>
#include<set>
#include<time.h>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<string>
#include<cstring>
#include<functional>
#include<stack>
#include<map>
#include<queue>
#include<cstring>
#define mod (1000000000+7)
#define middle (l+r)>>1
#define SIZE 10000000+10
#define lowbit(x) (x&(-x))
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long ll;
typedef long double lb;
const int inf_max = 0x3f3f3f3f;
const ll Linf = 9e18;
const int maxn = 100+10;
const long double nature = 2.7182818;
const double eps=0.0001;
using namespace std;
int V,n,cost[maxn],val[maxn],dp[40000],fv[maxn][5],fw[maxn][5];
// 思路:枚举每一个主件,在每个情况下更新最大值
void DP()
{
for(int i=1;i<=n;i++)
for(int j=V;j>=cost[i];j--) //逆序枚举,因为只能拿一个主件
{
dp[j]=max(dp[j],dp[j-cost[i]]+val[i]);
if(j>=cost[i]+fw[i][1]) //要拿附件1必须要拿主件
dp[j]=max(dp[j],dp[j-cost[i]-fw[i][1]]+val[i]+fv[i][1]);
if(j>=cost[i]+fw[i][2])
dp[j]=max(dp[j],dp[j-cost[i]-fw[i][2]]+val[i]+fv[i][2]);
if(j>=cost[i]+fw[i][1]+fw[i][2])
dp[j]=max(dp[j],dp[j-cost[i]-fw[i][2]-fw[i][1]]+val[i]+fv[i][1]+fv[i][2]);
}
}
void Initial()
{
memset(dp,0,sizeof(dp));
memset(fv,0,sizeof(fv));
memset(fw,0,sizeof(fw));
}
int main()
{
while(cin>>V>>n)
{
Initial();
for(int i=1;i<=n;i++)
{
int v,p,q;
cin>>v>>p>>q;
if(!q) //是主件
{
cost[i]=v;
val[i]=v*p;
}
else
{
fw[q][0]++;
fw[q][fw[q][0]]=v;
fv[q][fw[q][0]]=v*p;
}
}
DP();
printf("%d\n",dp[V]);
}
return 0;
}