其他分享
首页 > 其他分享> > [NOIP2006 提高组]金明的预算方案(01背包)

[NOIP2006 提高组]金明的预算方案(01背包)

作者:互联网

01 背包转移时的途径只有两种:不选,则直接跳过,考虑下一个;选,则直接加上对应的价值。本题“主件与附件”,则增加了几种状态(只枚举主件)

设 \(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]);

下面附有常见背包问题模板:

  1. 滚动数组优化 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]);
  1. 滚动数组优化完全背包(每种物品数目无限):
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]);
  1. 多重背包(每种物品个数有限):在时间复杂度要求不严格的情况下,可以转化为 01 背包处理(把同种物品视为不同物品)。

  2. 分组背包(物品分成 \(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