01背包
0-1背包问题是指每一种物品都只有一件,可以选择放或者不放。
- 方法一
V(i,j)表示前i种物品恰放入一个容量为j的背包的最大价值,因此状态转移方程:
- j<w(i) V(i,j)=V(i-1,j)
- j>=w(i) V(i,j)=max{ V(i-1,j),V(i-1,j-w(i))+v(i) }
for(int i = 0; i <= n; i++)//初始化第0列
V[i][0] = 0;
for(int j = 0; j <= C; j++)//初始化第0行
V[0][j] = 0;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= C; j++)
if(j < a[i-1].wight)
V[i][j] = V[i-1][j];
else
V[i][j] = MAX(V[i-1][j],V[i-1][j-a[i-1].wight] + a[i-1].value);
for(int i = n,j = C; i > 0; i--){
if(V[i][j] > V[i-1][j]){//将是否放入背包的n位向量赋值
x[i-1] = 1;
j = j - a[i-1].wight;
}
else
x[i-1] = 0;
}
- 方法二
可以使用一维数组存储,从上一种方法可以看出,在计算v[i][j]时只使用了v[i-1][0……j],所以使用一维滚动数组依次覆盖即可
for (int i=1;i<=n;i++)
for (int j=C;j>=0;j--){
if(t[i]<=j)
v[j]=max(v[j],v[j-a[i]]+w[i]);
else v[j]=v[j];
}
内层循环是逆序的原因:
我们在求v[j]的时候需要用到v[j-1],如果采用正序,当到v[j]时v[j-1]已经是这一行的状态了,没办法与前一行再进行比较。
完全背包
完全背包问题是指每种物品都有无限件可以放入背包。完全背包问题与01背包问题的区别在于完全背包每一件物品的数量都有无限个,而01背包每件物品数量只有1个。
- 方法一
在01背包的基础上加入变量k代表某物品放入多少个,状态转移方程:
v[i][j] = max{v[i][j],v[i-1][j - k * a[i]] + k * w[I]} (0<=k*a[i]<=v)
缺点:这个方法需要三层循环 - 方法二
转化为01背包问题。
第i种物品最多能够放入V/a[i]件,因此把第i种物品转换为重量相同的V/a[i]件物品,再解01背包问题即可。状态转移方程:
v[i][j]=max(v[i-1][j],v[i][j-a[i]]+w[i])
max函数第二个变量变化的原因:不是在上一种物品即i-1种物品的基础上放入了,而是第i件物品多放入一件
for (int i=1; i<=n; i++)
for (int j=1; j<=C; j++)
if (a[i] <= j)
v[i][j] = max(v[i-1][j],v[i][j-a[i]]+w[i]);
else
v[i][j] = v[i-1][j];
- 方法三
使用一维数组。不同于01背包,多放入一件第i类物品,就要在未放入此物品的基础上,因此使用顺序循环。状态转移方程:
v[j] = max(v[j],v[j-a[i]]+w[i])
for (int i=1;i<=n;i++)
for (int j=w[i];j<=v;j++)
v[j] = max(v[j],v[j-a[i]]+w[i]);
多重背包
多重背包问题中,每种物品的数量是有限的。
- 方法一
把第i种物品换成n[i]件01背包中的物品,则变成了01背包问题 - 方法二
第i种物品有n(i)+1种放入方式,即取0件、1件……n(i)件,状态转移方程:
v[i][j] = max ( v[i-1][j-k * a[i] ] + k*w[i] ) 0<=k<=num[i]
for (int i=1;i<=n;i++){
f[i][0]=0;
for (int j=a[i];j<=v;j++){
int ncount=min(num[i],j/a[i]);
for (int k=0;k<=ncount;k++){
v[i][j]=max(v[i][j],v[i-1][j-k*a[i]]+k*w[i]);
}
}
}