0-1背包
题目:有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。
第 i 件物品的体积是 vi,价值是 wi。求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出最大价值。
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
/*
0-1背包
每种物品有两种状态选或者不选,要求实现给定容积物品价值最大
*/
const int N = 4; //物品个数
const int V = 5; //背包容积
int w[N] = {2,4,4,5}; //物品价值
int v[N] = {1,2,3,4}; //物品体积
int knapsack(){
//dp[i][j]是考虑前i件物品并且体积为j时的价值量最优解
vector<vector<int> > dp(N+1,vector<int>(V+1)); //dp初始化
for(int i = 1;i <= N;i ++){
for(int j = 1;j <= V;j ++){
//dp状态转移:加入第i件物品时,价值量变为i价值量+除去i体积的容积的价值量,横向纵向与之前的价值量做对比
if(j< v[i-1]) dp[i][j] = dp[i-1][j];
else dp[i][j] = max(max(dp[i-1][j],dp[i][j-1]),dp[i-1][j-v[i-1]]+w[i-1]);
}
}
//打印状态转移矩阵
for(int i = 1;i <= N;i ++){
for(int j = 1;j <= V;j ++) cout<<dp[i][j]<<" ";
cout<<endl;
}
//返回结果
return dp[N][V];
}
int main(){
cout<<knapsack();
}
运行结果:
时间复杂度:O(N*V),空间复杂度O(N*V),并且时间上已经无法再优化,但空间上还可以再优化。
状态 i 只与状态 i - 1 有关,状态 i 之后的 i + 1 状态只能用到 i 。故可以将其优化为一维数组,状态转移方程由
dp[i][j] = max(max(dp[i-1][j],dp[i][j-1]),dp[i-1][j-v[i-1]]+w[i-1]);
优化为:
dp[j] = max(dp[j],dp[j-v[i-1]]+w[i-1]);
我们之前的 dp[i][j] 要么是从 dp[i-1][j] 得到的,要么是从 dp[i-1][j-v[i-1]]+w[i-1] 得到的。我们背包问题的思想就是当放体积为 v[i] 物品时,背包被划分为 v[i] 和 j - v[i]。为了不影响 dp[i-1] 到 dp[i] 的过渡,体积遍历应该从 v 到 v[i] 来走。
并且体积从后往前遍历还会保证 dp[N][V] 是最优解,因为它代表的意思就是最大容量情况下拿不拿第 i 个物品的总价值。
时间复杂度:O(N*V),空间复杂度O(V)。
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
/*
0-1背包
每种物品有两种状态选或者不选,要求实现给定容积物品价值最大
*/
const int N = 4; //物品个数
const int V = 5; //背包容积
int w[N] = {2,4,4,5}; //物品价值
int v[N] = {1,2,3,4}; //物品体积
int knapsack(){
//dp[j]是考虑前i件物品并且体积为j时的价值量最优解
vector<int> dp(V+1,0); //dp初始化
for(int i = 1;i <= N;i ++){
for(int j = V;j >= v[i-1];j --){
//dp状态转移:加入第i件物品时,价值量变为i价值量+除去i体积的容积的价值量
dp[j] = max(dp[j],dp[j-v[i-1]]+w[i-1]);
//打印dp
for(int k = 1;k <= V;k ++) cout<<dp[k]<<" ";
cout<<endl;
}
}
//返回结果
return dp[V];
}
int main(){
cout<<knapsack();
}
运行结果: