0-1背包
描述:
有N件物品和一个容量为M的背包。第i件物品的重量是w[i],价值是v[i]。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。
思路:
用子问题定义状态:即 f[i][j] f [ i ] [ j ] 表示前i件物品恰放入一个容量为j的背包可以获得的最大价值。
- 当 j<w[i] j < w [ i ] 时,表示 w[i] w [ i ] 不能放入,此时 f[i][j]=f[i−1][j] f [ i ] [ j ] = f [ i − 1 ] [ j ]
- 当 j>w[i] j > w [ i ] 时,表示 w[i] w [ i ] 可以放入,此时分为两种情况,且对应的最大价值分别为:
-
- 放: f[i][j]=f[i−1][j−w[i]]+v[i] f [ i ] [ j ] = f [ i − 1 ] [ j − w [ i ] ] + v [ i ]
-
- 不放: f[i][j]=f[i−1][j] f [ i ] [ j ] = f [ i − 1 ] [ j ] 。
-
- 综上:则最终状态转移方程是: f[i][j]=max{f[i−1][j],f[i−1][j−w[i]]+v[i]} f [ i ] [ j ] = max { f [ i − 1 ] [ j ] , f [ i − 1 ] [ j − w [ i ] ] + v [ i ] } 。
#include<iostream>
using namespace std;
int main(){
int w[6]={0,2,2,6,5,4};
int v[6]={0,6,3,5,4,6};
int m[6][11];
int M=10;
int N=5;
int res[5];
// 状态转移方程
for(int i=1;i<=N;i++){
for(int j=1;j<=M;j++){
if(j>w[i])
m[i][j]=max(m[i-1][j-w[i]]+v[i],m[i-1][j]);
else
m[i][j]=m[i-1][j];
}
}
// 判断选择哪个物品
for(int i=N;i>1;i--){
if(m[i][M]==m[i-1][M])
res[i]=0;
else{
res[i]=1;
c-=w[i];
}
res[1]=(m[1][M]>0)?1:0;
}
// 输出最大值矩阵
for(int i=1;i<=N;i++){
for(int j=1;j<=M;j++)
cout<<m[i][j]<<' ';
cout<<endl;
}
// 输出选择结果
for(int i=1;i<=N;i++){
cout<<res[i]<<' ';
}
}
完全背包
描述:
有N种物品和一个容量为M的背包,每种物品个数不限。第i种物品的重量是w[i],价值是v[i]。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。
思路:
完全背包问题转化为0-1背包:虽然每种物品的个数不限,但是由于背包体积的限制,每种物品最大量为 k=Mw[i] k = M w [ i ] ,即可以看做有k个重量价值均相同的物品供挑选,将上述问题转为0-1背包,但是相当于加了一层内循环,时间复杂度 O(NM∑(jw[i])) O ( N M ∑ ( j w [ i ] ) ) ,空间复杂度 O(NM) O ( N M ) 。
for(int i=1;i<N;i++){
for(int j=1;j<=M;j++){
for(int k=0;k<=M/w[i]];k++){
if(j>=k*w[i]){
m[i][j]=max(m[i-1][j],m[i-1][j-k*w[i]]+k*v[i]);
}
}
}
}
简单优化:
若两件物品满足 w[i]>w[j] w [ i ] > w [ j ] && v[i]<v[j] v [ i ] < v [ j ] ,则可以过滤掉物品i。
优化时间复杂度为O(NM):
定义 m[i][j] m [ i ] [ j ] 表示在前i种物品中选择若干件物品放入容量为i的背包的最大价值。对于第i种物品的出现,我们对第i种物品进行决策。如果选择不放,则 m[i][j]=m[i−1][j] m [ i ] [ j ] = m [ i − 1 ] [ j ] ;如果选择放,则背包中应该至少出现一件物品i,故 m[i][j]=m[i][j−w[i]]+v[i] m [ i ] [ j ] = m [ i ] [ j − w [ i ] ] + v [ i ] 。因为 m[i][j−w[i]] m [ i ] [ j − w [ i ] ] 中可能有第i种物品,也有可能没有,因此要预留 w[i] w [ i ] 的空间来存放第i种物品。
for(int i=1;i<N;i++){
for(int j=1;j<=M;j++){
m[i][j]=m[i-1][j];
if(j>=w[i]){
m[i][j]=max(m[i][j],m[i][j-w[i]]+v[i]);
}
}
}
多重背包
描述:
有N种物品和一个容量为M的背包。第i种物品的重量是w[i],价值是v[i],个数为n[i]。求解将哪些物品装入背包可使这些物品的重量总和不超过背包容量,且价值总和最大。
思路1:
同完全背包的第一个思路,只是在第三重循环中,对k进行约束。
for(int i=1;i<=N;i++){
for(int j=1;j<=M;j++){
for(int k=0;k<=M/w[i]&&k<=n[i];k++){
if(j>=k*w[i]){
m[i][j]=max(m[i-1][j],m[i-1][j-k*w[i]]+k*v[i]);
}
}
}
}