背包九讲之——多重背包

多重背包

多重背包是限制了每种物品数量的完全背包,因此可以按照完全背包的思路来做(完全背包见上篇博客)。
dp[i][j]表示前i种物品在容量为j的情况下最大价值,那么就有ki+1种情况:
1.不选:dp[i][j]=dp[i-1][j];
2.选一件:dp[i][j]=dp[i-1][j-v[i]]+w[i];
3.选两件:dp[i][j]=dp[i-1][j-2v[i]]+2w[i];

ki+1.选ki件:dp[i][j]=dp[i-1][j-kiv[i]]+kiw[i];
->dp[i][j]=max{dp[i-1][j-kv[i]]+kw[i]};0<=k<=ki;
那就开始遍历吧!

dp[0][0]=0;
for(int i=1;i<=n;i++)
{
	for(int j=0;j<=m;j++)
	{
		for(int k=0;k<=ki && k*v[i]<=j;k++)
		{
			dp[i][j]=max(dp[i][j],dp[i-1][j-k*v[i]]+k*w[i]);
		}
	}
}
cout<<dp[n][m];

显然这种做法是正确的,但是并不理想,复杂度太高O(nmk),差不多为O(n3)的样子,我们继续进行优化。
考虑一种思想:将第i种物品分成ki个数量为1的物品,化成01背包,使用01背包的办法解答,这种想法当然是很好的,但是算来复杂度并无变化。那么我们就不能把ki分成ki个1,需要进行其他化法——二进制化简。其实质是二进制可以表示任何一个十进制整数,例如 11,我们化成:1 2 4 4,我们使用1 2 4 4这四个数随意组合,可以组合出0~11.其中前三个数是二进制依次分出,而第三个数应当分出8,但是不足,那么就取剩下的数。开分!

struct love
{
	int v,w;
};
int main()
{
	dp[0]=0;
	vector<love> wife;
	for(int i=1;i<=n;i++)
	{
		for(int k=1;k<=ki;k*=2)//将ki二进制划分成log ki个物品
		{
			ki-=k;
			wife.push_back({v[i]*k,w[i]*k});
		}
		if(ki)	wife.push_back({v[i]*ki,w[i]*ki});
	}
	//现在就已经转化为01背包问题(见前两篇博客)
	for(auto it:wife)//也可用迭代器噢
	{
		for(int j=m;j>=it.v;j--)//01背包的从大往小遍历
		{
			dp[j]=max(dp[j],dp[j-it.v]+it.w);
		}
	}
	cout<<dp[m];
}

我发的代码都是不完整的核心代码,补充好基本定义输入输出是可以直接使用的。
以上就是多重背包了,若有错误还望指出,另外多重背包还有单调队列的更好的优化方法,蒟蒻不会。。。各位dalao可以看看别人的博客,谢谢!

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值