[NOIP2006 提高组]金明的预算方案(01背包)
作者:互联网
01 背包转移时的途径只有两种:不选,则直接跳过,考虑下一个;选,则直接加上对应的价值。本题“主件与附件”,则增加了几种状态(只枚举主件):
- 不选,直接下一种;
- 只选这一主件;
- 选这一主件和其对应的附件 1;
- 选这一主件和其对应的附件 2;
- 选这一主件和其对应的两个附件。
设 \(Wmai,Cmain,Wacc,Cacc\) 四个数组,分别记录主件体积、主件价值、主件对应的附件 1, 2 体积,主件对应的附件 1,2 价值。于是有如下四个转移方程(当然滚动数组):
\( dp_j=\max(dp_j,dp_{j-Wmain_i}+Cmain_i)\\ dp_j=\max(dp_j,dp_{j-Wmain_i-Wacc_{i,0}}+Cmain_i+Cacc_{i,0})\\ dp_j=\max(dp_j,dp_{j-Wmain_i-Wacc_{i,1}}+Cmain_i+Cacc_{i,1})\\ dp_j=\max(dp_j,dp_{j-Wmain_i-Wacc_{i,0}-Wacc_{i,1}}+Cmain_i+Cacc_{i,0}+Cacc_{i,1}) \)
注意每个方程都有对应的边界范围,即考虑剩余空间是否能容纳。
下面是 AC 代码片段:
int m,n; scanf("%d%d",&m,&n);//钱数=空间, 物品数
for(int i=1,w,p,q;i<=n;++i)
{
scanf("%d%d%d",&w,&p,&q);//花费=体积,重要度(和价格相乘算出收益),对应主件
if(!q)
{
Wmain[i]=w;
Cmain[i]=w*p;
}
else
{
++Wacc[q][0];//记录是第几个附件
Wacc[q][Wacc[q][0]]=w;
Cacc[q][Wacc[q][0]]=w*p;
}
}
for(int i=1;i<=n;++i)
{
for(int j=m;Wmain[i]/*判断是否是主件*/&&j>=Wmain[i];--j)
{
dp[j]=max(dp[j],dp[j-Wmain[i]]+Cmain[i]);
//只选主件
if(j>=Wmain[i]+Wacc[i][1])
dp[j]=max(dp[j],dp[j-Wmain[i]-Wacc[i][1]]+Cmain[i]+Cacc[i][1]);
//选主件和附件1
if(j>=Wmain[i]+Wacc[i][2])
dp[j]=max(dp[j],dp[j-Wmain[i]-Wacc[i][2]]+Cmain[i]+Cacc[i][2]);
//选主件和附件2
if(j>=Wmain[i]+Wacc[i][1]+Wacc[i][2])
dp[j]=max(dp[j],dp[j-Wmain[i]-Wacc[i][1]-Wacc[i][2]]+Cmain[i]+Cacc[i][1]+Cacc[i][2]);
//选主件、附件1、附件2
}
}
printf("%d\n",dp[m]);
下面附有常见背包问题模板:
- 滚动数组优化 01 背包(各个物品不同):
for(int i=1;i<=n;++i)
for(int j=m;j>=v[i];--j)//滚动数组,倒序跑以减免之前的影响、利用上一层的答案
dp[j]=max(dp[j],dp[j-v[i]]+c[i]);
- 滚动数组优化完全背包(每种物品数目无限):
for(int i=1;i<=n;++i)
for(int j=v[i];j<=m;++j)//正序跑,因为完全背包是同种物品之间更新
dp[j]=max(dp[j],dp[j-v[i]]+c[i]);
-
多重背包(每种物品个数有限):在时间复杂度要求不严格的情况下,可以转化为 01 背包处理(把同种物品视为不同物品)。
-
分组背包(物品分成 \(k\) 组,每组中的物品互相冲突,只能选一件):
for(int i=1;i<=k;++i)//枚举每一组
for(int j=v;j>=0;--j)
for(auto k:a[i])//c++11特性:for-each循环
dp[j]=max(dp[j],dp[j-v[k]]+c[k]);
THE END
标签:Cmain,NOIP2006,01,主件,max,Wacc,Wmain,dp,金明 来源: https://www.cnblogs.com/q0000000/p/15430298.html