一,01背包
问题描述:有n个物体,它们有各自的重量和价值,现有给定容量的背包,如何让背包里装入的物品具有最大的价值总和?
思路:定义f[i][j]表示当前背包容量为j,前i个物品最佳组合对应的价值,v[i]为物品的价值,w[i]为物品的重量,n为物品数量,c为背包容量,有下面两种情况
(1)j<w[i] f[i][j]=f[i-1][j]
(2)j>w[i] f[i][j]=max(f[i-1][j],f[i-1][j-w[i]]+v[i])
在void find_max(){
int i,j;
for(i=1;i<=n;i++){
for(j=1;j<=c;j++){
if(j<w[i]){
f[i][j]=f[i-1][j];//物品装不进去
}
else{
f[i][j]=max([f[i-1][j],f[i-1][j-w[i]]+v[i]);
}
}
}
代码优化:f[i][j]的转移只与f[i][x]有关,f[i][x]是由前一次循环得到,所以可将f压缩成一维数组,f[j]表示容量为j时的最大价值,所以f[j]=max(f[j],f[j-w[i]]+v[i]),c的循环应从答到小,以免本次f[j]的值被本次更新的f[j-w[i])影响
在这里void find_max_better(){
int i,j;
for(i=1;i<=n;i++){
for(j=c;j>=w[i];j--){
if(f[j]<=f[j-w[i]]+v[i]){
f[j]=f[j-w[i]]+v[i];
}
}
}
}
二,完全背包
完全背包问题是指每种物品都有无限件
void complete(){
for(int i=1;i<=n;i++){
for(int j=w[i];j<=c;j++){
f[j]=max(f[i],f[j-w[i]]+value[i]);
}
}
}
三,多重背包
多重背包限定的每种物品的数量,例如给有价值为2数量为10的物品,我们可以把10拆开,拆成以二进制表示的数,二进制可以表示任何数。所以10就可以有1,2,4,8之内的数表示,一开始选1,然后让10-1=9,再选2,9-2=7,再选4,7-4=3,因为3<8,所以10由1,2,4,3组成,求解方式可由01背包和完全背包组合,因为当数据大于背包容量时是可以任意取的,所以看作完全背包
在#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define N 1000 //物品个数
#define M 100000000 //所有物品可能的最大价值
int m[N],c[N],w[N],f[M];
int V;
int max(int a,int b){return a>b?a:b;}
void ZeroOnePack(int cost,int weight)
{
int v;
for(v=V;v>=cost;v--) f[v]=max(f[v],f[v-cost]+weight);
}
void CompletePack(int cost,int weight)
{
int v;
for(v=cost;v<=V;v++)
f[v]=max(f[v],f[v-cost]+weight);
}
void MultiplePack(int cost,int weight,int amount)
{
int k;
if(cost*amount>=V)
{
CompletePack(cost,weight);
return;
}
k=1;
while(k<amount)
{
ZeroOnePack(k*cost,k*weight);
amount=amount-k;
k=k*2;
}
ZeroOnePack(amount*cost,amount*weight);
}
int main()
{
int n,i;
scanf("%d %d",&n,&V);
// 两种不同的初始化方式,根据情况自行选择
//memset(f,0,sizeof(f[0])*(V+1)); // 只希望价格尽量大
//memset(f,-M,sizeof(f[0])*(V+1));f[0]=0; // 要求恰好装满背包
for(i=0;i<n;i++) scanf("%d %d %d",m+i,c+i,w+i);
for(i=0;i<n;i++) MultiplePack(c[i],w[i],m[i]);
printf("%d\n",f[V]);
system("PAUSE");
return 0;
}