揹包問題(3)-多重揹包

多重揹包
題目描述:
有N種物品和一個容量爲V的揹包。第i種物品最多有n[i]件可用,每件費用是c[i],價值是w[i]。求解將哪些物品裝入揹包可使這些物品的費用總和不超過揹包容量,且價值總和最大。

算法分析:
暴力:
多重揹包問題的思路跟完全揹包的思路非常類似,只是k的取值是有限制的,因爲每件物品的數量是有限制的,狀態轉移方程爲:

dp[i][v]=max{dp[i - 1][v-k*c[i]] + w[i] | 0 <=k <= n[i]}
其中c[i]是物品的數量,和完全揹包的不同支出在於完全揹包可以取無數件,而多重揹包給定了最多能取的數量。這樣也是三個循環,分別是揹包容量,物品個數和物品種類。

#include<iostream>
using namespace std;
int w[510],c[510],s[510],f[6010];
int main()
{
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>w[i]>>c[i]>>s[i];
	for(int i=1;i<=n;i++)
	for(int j=m;j>=0;j--)
	for(int k=0;k<=s[i]&&j-k*w[i]>=0;k++)
		f[j]=max(f[j],f[j-k*w[i]]+k*c[i]);
	cout<<f[m]<<endl;
	return 0;
}

如果每件物品的數量比較多時,很明顯這個做法會超時,所以我們要想更優化的方法。
二進制優化:
 轉化爲01揹包求解:把第i種物品換成n[i]件01揹包中的物品。考慮二進制的思想,考慮把第i種物品換成若干件物品,使得原問題中第i種物品可取的每種策略——取0…n[i]件——均能等價於取若干件代換以後的物品。另外,取超過n[i]件的策略必不能出現。

方法是:將第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]間的每一個整數,均可以用若干個係數的和表示。

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#define fre(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout);
using namespace std;
const int MAX=2147483647;
const int N=1e4;
struct node
{
	int v,w;
} a[N];
int n,C,V,W,cnt,tot,f[N]; 
void input()
{
	scanf("%d%d",&C,&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d%d",&W,&V,&cnt);
		for(int j=1;j<=cnt;j*=2)
		{
			a[++tot].w=W*j;
			a[tot].v=V*j;
			cnt-=j;
		}
		if(cnt) a[++tot].w=W*cnt,a[tot].v=V*cnt;
	}
}
void Knapsack()
{
	for(int i=1;i<=tot;i++)
	for(int j=C;j>=a[i].w;j--)
		f[j]=max(f[j],f[j-a[i].w]+a[i].v);
}
int main()
{
	//fre();
	input();
	Knapsack(); 
	printf("%d\n",f[C]);
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章