动态规划8_多重背包

接下来是多重背包(MultiplePack),

问题:有N种物品和一个容量为V的背包,第i件物品最多有n[i]件可用,每件的费用是w[i],价值是v[i]。

将物品放入背包中,使得总容量不超过V,而且总价值最大。


因为对于第i种物品有n[i]+1种策略:取0件,取1件……取n[i]件。

令dp[i][w]表示前i种物品恰放入一个容量为w的背包的最大权值,

则有状态转移方程:

dp[i][w]=max{dp[i-1][w-k*w[i]]+k*v[i]|0<=k<=n[i]}


代码实现:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

const int MAXN=300+10;
const int MAXW=300+10;

int dp[MAXW];
int n[MAXN],v[MAXN],w[MAXN];

int main()
{
	//freopen("in.txt","r",stdin);
	int N,W;
	while(scanf("%d %d",&N,&W)!=EOF)
	{
		for(int i=0;i<N;i++)
			scanf("%d %d %d",&v[i],&w[i],&n[i]);

		memset(dp,0,sizeof(dp));

		for(int i=0;i<N;i++)
		{
			for(int j=0;j<n[i];j++)
			{
				for(int k=W;k>=w[i];k--)
				{
					dp[k]=max(dp[k],dp[k-w[i]] + v[i]);
				}
			}
		}
		printf("%d\n",dp[W]);
	}
	return 0;
}


HDU2191
题意:多重背包价格和重量交换了,要获取最大的重量。

代码实现:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

const int MAXN=300+10;
const int MAXW=300+10;

int dp[MAXW];
int n[MAXN],v[MAXN],w[MAXN];

int main()
{
	//freopen("in.txt","r",stdin);
	int N,V;
	int T;
	scanf("%d",&T);
	while(T--)
	{
	    scanf("%d %d",&V,&N);
		for(int i=0;i<N;i++)
			scanf("%d %d %d",&v[i],&w[i],&n[i]);

		memset(dp,0,sizeof(dp));

		for(int i=0;i<N;i++)
		{
			for(int j=0;j<n[i];j++)
			{
				for(int k=V;k>=v[i];k--)
				{
					dp[k]=max(dp[k],dp[k-v[i]] + w[i]);
				}
			}
		}
		printf("%d\n",dp[V]);
	}
	return 0;
}


其实可以将多重背包转化为0-1背包

例如第i件物品有num个,将其变成num个属性(价格,重量)跟其一样的物品就行。然后用0-1背包的思想去做。

时间复杂度是O(W*(num1+num2+....num(n))).


用hdu2191的代码实现:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

const int MAXN=300000+10;

int dp[MAXN];
int num,v[MAXN],w[MAXN];

int main()
{
	//freopen("in.txt","r",stdin);
	int N,V,vv,ww;
	int T;
	scanf("%d",&T);
	while(T--)
	{
	    scanf("%d %d",&V,&N);
		int cnt=0;
		for(int i=0;i<N;i++)
		{
			scanf("%d %d %d",&vv,&ww,&num);

			//分成num个物品
			for(int j=0;j<num;j++)
			{
				v[cnt]=vv;
				w[cnt]=ww;
				cnt++;
			}
		}

		memset(dp,0,sizeof(dp));

		//0-1背包
		for(int i=0;i<cnt;i++)
		{
			for(int j=V;j>=v[i];j--)
			{
				dp[j]=max(dp[j],dp[j-v[i]] + w[i]);
			}
		}
		printf("%d\n",dp[V]);
	}
	return 0;
}


多重背包二进制拆分


方法是将物品i分成若干件,其中每一件物品都有一个系数,这件物品的费用和价值都是原来的费用和价值乘以这个系数,

使得这些系数分别为1,2,4,…,2^(k-1),n[i]-2^k+1,且k满足n[i]-2^k+1>0的最大整数。

例如,n[i]=13,就将该物品拆成系数为1、2、4、6的四件物品。分成的这几件物品的系数和为n[i],

表明不可能取多于n[i]件的第i种物品。另外这种方法也能保证对于0..n[i]间的每一个整数,均可以用若干个系数的和表示。

时间复杂度是(w*log(num1+log(num2)+....log(num(n))))


还是用hdu2191的代码去实现:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;

const int MAXN=300000+10;

int dp[MAXN];
int num,v[MAXN],w[MAXN];

int main()
{
	//freopen("in.txt","r",stdin);
	int N,V,vv,ww;
	int T;
	scanf("%d",&T);
	while(T--)
	{
	    scanf("%d %d",&V,&N);
		int cnt=0;
		for(int i=0;i<N;i++)
		{
			scanf("%d %d %d",&vv,&ww,&num);

			//二进制分解num个物品
			for(int j=1;j<=num;j*=2)
			{
				v[cnt]=vv*j;
				w[cnt]=ww*j;
				cnt++;
				num-=j;
			}
			if(num>0)
			{
				w[cnt]=num*ww;
				v[cnt++]=num*vv;
			}
		}

		memset(dp,0,sizeof(dp));

		//0-1背包
		for(int i=0;i<cnt;i++)
		{
			for(int j=V;j>=v[i];j--)
			{
				dp[j]=max(dp[j],dp[j-v[i]] + w[i]);
			}
		}
		printf("%d\n",dp[V]);
	}
	return 0;
}

据说还有用单调队列去实现多重背包,时间复杂度是O(W*N),还没有去了解。等学了再加入到这里。

推荐题目:POJ1276


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值