1.01背包
有n种物品,一个容量为c的背包,每种物品的体积是v,重量是w,且每种物品只有一件,可以选择放或者不放。求那些物品放入背包可以让背包重量最大。
初始化问题:这里有两种情况1)恰好装满:这时就只有背包容量为0的可以被0个物品恰好装满,所以容量为零的背包初始化为0,其余的均不符合合法状态所以初始化为负无穷大。2)不要求恰好装满:这时每个容量的背包都有什么都不装的合法解所以初始化为0。
例题:http://codevs.cn/problem/5709/
有两种方式:
1)
#include<iostream>
#include<string.h>
using namespace std;
const int MAX=1000+10;
int dp[MAX][MAX];
int main()
{
int n,c,v,w;
memset(dp,0,sizeof(dp));
cin>>c>>n;
for(int i=1; i<=n;i++)
{
cin>>v>>w;
for(int j=1;j<=c;j++)
{
if(j>=v)
{
dp[i][j]=max(dp[i-1][j],dp[i-1][j-v]+w);
}
else
{
dp[i][j]=dp[i-1][j];
}
}
}
cout<<dp[n][c]<<endl;
}
这里dp[i][j]数组的i表示前几种物品,j表示背包容量
2)
#include<iostream>
using namespace std;
const int MAX = 1000+10;
int dp[MAX];
int main()
{
int n,c,v,w;
cin>>c>>n;
for(int i=0; i<n;i++)
{
cin>>v>>w;
for(int j=c; j>=v;j--)
{
dp[j]=max(dp[j],dp[j-v]+w);
}
}
cout<<dp[c]<<endl;
return 0;
}
这里将二维数组变为一维进行空间优化,注意的是,需要逆序推,这样之前的数组元素就是上一个状态的。
两种方式中第二重循环是核心,是01背包的子问题。
总结一个01背包核心函数:
void ZeroOne_Pack(int volume,int weight,int n)
{
for(int i=n; i>=volume; i--)
dp[i] = max(dp[i],dp[i-volume] + weight);
}
2.完全背包
有N种物品和一个容量为c的背包,每种物品都有无限件可用。
第i种物品的体积是v,价值是w。求解将哪些物品装入背包可使这些物品的体积总和不超过背包容量,且价值总和最大。
完全背包与01背包类似,不同的是01背包选择放或不放,而完全背包是选择放多少个,完全背包与01背包只是推的顺序不一样。01背包之所以逆序推是因为第i次循环是由第i-1次推出的,从而保证考虑选择第i个物品是依据之前未选择i这个物品的子问题。而完全背包恰好相反,它可能要依据之前选了i物品的子问题。
核心函数:
void Complete_Pack(int volume,int weight,int n)
{
for(int i=volume; i<=n; i++)
dp[i] = max(dp[i],dp[i-volume] + weight);
}
3.多重背包
有N种物品和一个容量为c的背包。第i种物品最多有n件可用,每件体积是v,价值是w。求解将哪些物品装入背包可使这些物品的体积总和不超过背包容量,且价值总和最大。
这里用了一个定理:
一个正整数n可以被分解成1,2,4,…,2^(k-1),n-2^k+1(k是满足n-2^k+1>0的最大整数)的形式,且1~n之内的所有整数均可以唯一表示成1,2,4,…,2^(k-1),n-2^k+1中某几个数的和的形式。
int Multiple_Pack(int v[],int w[],int c[],int n,int m) //v是体积,w是重量,c是数量,n是物品种类,m是背包容量
{
memset(dp,0,sizeof(dp));
for(int i=1; i<=n; i++)
{
if(c[i]*v[i] > m)
Complete_Pack(v[i],w[i],m);
else
{
int k = 1;
while(k < c[i])
{
ZeroOne_Pack(k*v[i],k*w[i],m);
c[i] -= k;
k <<= 1;
}
ZeroOne_Pack(c[i]*v[i],c[i]*w[i],m);
}
}
return dp[m];
}