多重背包问题

多重背包问题

多重背包的题目(杭电2191题):http://acm.hdu.edu.cn/showproblem.php?pid=2191
《背包九讲》
有 N 种物品和一个容量为 V 的背包。第 i 种物品最多有 M i 件可用,每件耗费的空间是 C i ,价值是 W i 。求解将哪些物品装入背包可使这些物品的耗费的空间总和不超过背包容量,且价值总和最大。
基本算法
这题目和完全背包问题很类似。基本的方程只需将完全背包问题的方程略微一改即可。因为对于第 i 种物品有 M i +1 种策略:取 0 件,取 1 件……取 M i 件。令 F[i,v] 表示前 i 种物品恰放入一个容量为 v 的背包的最大价值,则有状态转移方程:F[i , v] = max {F[i − 1,v − k ∗ C i ] + k ∗ W i | 0 ≤ k ≤ M i },复杂度是 O(V ΣM i ) 。

模板:

同样先考虑情景:dp[w][i]
1.当c[i]*d[i]>=w时,可以把第i类的东西看成是无限的,即是一个完全背包问题
2.当c[i]*d[i]<w时,可以把第i类的东西看成1个1个,即d[i]个01背包问题,当d[i]数量太多时,明显效率太低,因此可以把1个1个,看成1个,2个,4个…,2x个,d[i]-[2(x+1)-1]个(2^(x+2)-1>d[i])这样对于1…d[i]的任意个数,都可由1,2,4,8…组成,即考虑了第i组,1,2,3…d[i]的每种情况,这样求出的最优解也必定是最优解,同样再对其进行压缩,按各种的情景进行求解。
3、更高级的是楼天城的单调队列法

1、转换成01背包(三重循环)

#include
#include
using namespace std;
int n,m;  //n为物品的种类数,m为背包最大承载重量 
int dp[1005];    // dp状态数组 
int num[1005];   //物品的数量 
int value[1005]; //物品的价值 
int weight[1005];//物品的重量 
void init()   //输入函数 
{
	memset(dp,0,sizeof(dp));  //初始化dp数组
	for(int i=0;i>weight[i];  //输入物品的重量 
	for(int i=0;i>value[i];   //输入物品的价值 
	for(int i=0;i>num[i];     //输入物品的个数 
}
int main()
{
	while(cin>>n>>m)
	{
		init();  //输入函数
		for(int i=0;i=weight[i];k--)   //当做01背包来处理 
		      {  //取01背包情况的dp[k]和dp[k-weight[i]]+value[i]的最大值 
		      	 if(dp[k]>dp[k-weight[i]]+value[i])   
		      	    dp[k]=dp[k];
		      	 else dp[k]=dp[k-weight[i]]+value[i];
			  }
		cout<

2、二进制优化法

#include
#include
using namespace std;
int n,m;  //n为物品的种类数,m为背包最大承载重量 
int dp[1005];    // dp状态数组 
int num[1005];   //物品的数量 
int value[1005]; //物品的价值 
int weight[1005];//物品的重量 
/*
多重背包问题的二进制解法 
*/
void init()   //输入函数 
{
	cin>>n>>m;
	memset(dp,0,sizeof(dp));  //初始化dp数组
	for(int i=0;i>weight[i];  //输入物品的重量 
	for(int i=0;i>value[i];   //输入物品的价值 
	for(int i=0;i>num[i];    //输入物品的个数 
	    
}
void bag01(int heft,int worth)  //01背包 
{   //heft为当前物品的重量,worth为当前物品的价值 
	for(int i=m;i>=heft;i--)
	{
		if(dp[i]>dp[i-heft]+worth)
		   dp[i]=dp[i];
		else dp[i]=dp[i-heft]+worth;
	}
}
void CompleteBag(int heft,int worth)  //完全背包 
{   //heft为当前物品的重量,worth为当前物品的价值 
	for(int i=heft;i<=m;i++)
	{
		if(dp[i]>dp[i-heft]+worth)    
		   dp[i]=dp[i];
		else dp[i]=dp[i-heft]+worth;
	}
}
int main()
{
	while(cin>>n>>m)
	{
		init();  //输入函数
		for(int i=0;i<=n;i++)  //遍历每一个物品 
		{
			if(weight[i]*num[i]>=m)  
			{   //如果同一类物品的总重量大于背包的最大容量,则转换成完全背包 
				CompleteBag(weight[i],value[i]);
			}
			else  //多个01背包 
			{
				for(int t=1;t*2-1<=num[i];t*=2)  //二进制的运用
				   bag01(weight[i]*t,value[i]*t);
				bag01(weight[i]*(num[i]-t+1),value[i]*(num[i]-t+1));
			}
		}
		cout<

本人开始对bag01(weight[i](num[i]-t+1),value[i](num[i]-t+1));这一条语句不甚理解,原来是因为t无法遍历到t=d[i],故此需要添加这一条语句。
举个例子,假设d[i]=6,一个for循环执行了bag01(weight[i]*1,value[i]*1)和bage01(weight[i]2,value[i]2),此时t=4跳出循环,此时已经有3个此物品已经放入背包了,所以我们补上了这一条语句bag01(weight[i](num[i]-t+1),value[i](num[i]-t+1));,而num[i]-t+1的值正好等于3,所以将最后的3的物品也加入了背包,所以6个物品都加入了背包之中,至于装了几个,则完全看bag01函数即背包是否装得下(这就是多重背包了,即多个01背包)。

你可能感兴趣的:(ACM算法)