多重背包的二进制优化

做题链接

多重背包的二进制优化

输入格式

第一行两个整数,\(N \ V\)用空格隔开,分别表示物品种数和背包容积。

接下来有 \(N\) 行,每行三个整数 \(vi,wi,si\),用空格隔开,分别表示第\(i\)种物品的体积、价值和数量。

输出格式

输出一个整数,表示最大价值。

数据范围

\(0<N≤1000\)
\(0<V≤2000\)
\(0<vi,wi,si≤2000\)

提示:

本题考查多重背包的二进制优化方法。

输入样例

4 5
1 2 3
2 4 1
3 4 3
4 5 2

输出样例:

10

思路

优化多重背包的优化

首先,我们不能用完全背包的优化思路来优化这个问题,因为每组的物品的个数都不一样,是不能像之前一样推导来优化递推关系的。

我们列举一下更新次序的内部关系:(温馨提示,记得将式子对齐后进行观看)

\(f[i , j ] = max( f[i-1,j] , f[i-1,j-v]+w , f[i-1,j-2*v]+2*w , f[i-1,j-3*v]+3*w , .....)\)

\(f[i , j-v]= max( f[i-1,j-v] , f[i-1,j-2*v] + w , f[i-1,j-3*v]+3*w , .....)\) 由上两式,可得出如下递推关系

\(f[i][j]=max(f[i,j-v]+w , f[i-1][j])\)

本题的优化方式

接下来,我介绍一个二进制优化的方法,假设有一组商品,一共有11个。我们知道,十进制数字 11 可以这样表示

\(11=1011(B)=0111(B)+(11−0111(B))=0111(B)+0100(B)\)

正常背包的思路下,我们要求出含这组商品的最优解,我们要枚举12次(枚举装\(0,1,2....11\)个)。

现在,如果我们把这11个商品分别打包成含商品个数为\(1\)个,\(2\)个,\(4\)个,\(4\)个(分别对应\(0001,0010,0100,0100\))的四个”新的商品 “, 将问

题转化为\(01\)背包问题,对于每个商品,我们都只枚举一次,那么我们只需要枚举四次 ,就可以找出这含组商品的最优解。 这样就大大减

少了枚举次数。

这种优化对于大数尤其明显,例如有\(1024\)个商品,在正常情况下要枚举\(1025\)次 , 二进制思想下转化成\(01\)背包只需要枚举\(10\)次。

优化的合理性的证明

先讲结论:上面的\(1,2,4,4\)是可以通过组合来表示出0~11中任何一个数的,还是拿\(11\)证明一下(举例一下):

首先,\(11\)可以这样分成两个二进制数的组合:

\(11=0111(B)+(11−0111(B))=0111(B)+0100(B)\)

其中\(0111\)通过枚举这三个\(1\)的取或不取(也就是对\(0001(B)\)\(0010(B)\)\(0100(B)\)的组合),可以表示十进制数0~7( 刚好对应了$ 1,2,4$ 可

以组合出 \(0~7\) ) , \(0~7\)的枚举再组合上\(0100(B)\)( 即 十进制的 4 ) ,可以表示十进制数 0~11。其它情况也可以这样证明。这也是为什么,这

个完全背包问题可以等效转化为\(01\)背包问题,有木有觉得很奇妙

怎么合理划分一个十进制数?

上面我把\(11\)划分为

\(11=0111(B)+(11−0111(B))=0111(B)+0100(B)\)

是因为 0111(B)刚好是小于11的最大的尾部全为1的二进制 ( 按照上面的证明,这样的划分没毛病 ) , 然后那个尾部全为1的数又可以 分解

\(0000....1 , 0000....10 , 0000....100\) 等等。

总结一下敲黑板

就是先将所有的物品读入进来,然后将每个物品的\(s\)通过二进制,划分成多个进行打包,形成新的物品(价值和体积也要随之变化),每

个原来的物品都这样进行处理最后再对打包后的物品做一遍01背包即可

代码

#include<bits/stdc++.h>
using namespace std;
struct node{
	int v,w;
};

vector<node> G;
const int N=20000;
int f[N];
int n,m;
int main()
{
	cin>>n>>m;
	int v,w,s;
	for(int i=1;i<=n;i++)
	{
		cin>>v>>w>>s;
		for(int k=1;k<=s;k*=2)
		{
			G.push_back({k*v,k*w});
			s-=k;
		}
		if(s>0) G.push_back({s*v,s*w});
	}
	for(int i=0;i<G.size();i++)
	{
		for(int j=m;j>=G[i].v;j--)
		{
			f[j]=max(f[j],f[j-G[i].v]+G[i].w);
		}
	}
	cout<<f[m]<<endl;
	return 0;
}
posted @ 2020-11-05 14:20  邦的轩辕  阅读(113)  评论(0编辑  收藏  举报