解题思路
1. 递 规(自顶向下)
#include<vector>
using namespace std;
class Knapsacko1 {
private:
//用[0...index ]的物品,填充容积为c 的背包的最大价值
int bestValue(const vector<int>&w,const vector <int>v,int index,int c){
if(index<0||c<=0)
return 0;
int res=bestValue(w,v,index-1,c);//第index个物品的重量超出了对应的容量c;
if(c>=w[index])
res=max(res,v[index]+bestValue(w,v,index,c-w[index]));
return res;
}
public:
int knapsack01(const vector<int>& w,const vector<int>& v,int C) {
int n=w.size();
return bestValue(w,v,n-1,C)//考虑将第n个物品添加到背包中
}
};
由于会出现(index,c)数据对而重复的计算,所以使用记忆化搜索对算法进行改进。因为有两个限制条件w[index],v[index]
,因此使用二维数组对其进行保存vector<vector<int>>memory
2. 递 规+记忆化搜索(自顶向下)
#include<vector>
using namespace std;
class Knapsacko1 {
private:
vector<vector<int>>memo;
//用[0...index ]的物品,填充容积为c 的背包的最大价值
int bestValue(const vector<int>&w,const vector <int>v,int index,int c){
if(index<0||c<=0)
return 0;
if(memo[index][c]!=-1)
return memo[index][c];
int res=bestValue(w,v,index-1,c);//第index个物品的重量超出了对应的容量c;
if(c>=w[index])
res=max(res,v[index]+bestValue(w,v,index,c-w[index]));
memo[index][c]=res;
return res;
}
public:
int knapsack01(const vector<int>& w,const vector<int>& v,int C) {
int n=w.size();
memo=vector<vector<int>(n,vector<int>(C+1,-1));
return bestValue(w,v,n-1,C)//考虑将第n个物品添加到背包中
}
};
3. 动态规化(自底向上)关于二维数组
右下角的方格便是从编号为[0,1,2]
三个物品中选择,使得容量不超过5
的情况下获得最大的价值。
#include<vector>
using namespace std;
class Knapsacko1 {
private:
public:
int knapsack01(const vector<int>& w,const vector<int>& v,int C) {
assert()
int n=w.size();
if(n=0)
return 0;
memo=vector<vector<int>(n,vector<int>(C+1,-1));
for(int j=0;j<=C;j++)
memo[0][j]=(j>=w[0]?v[0]:0);
for(int i=1;i<n;i++)
for(int j=0;j<=C;j++)
{ memo[i][j]=memo[i-1][j];//第一个策略,不放w[i]
if(j>=w[j])
memo[i][j]=max(memo[i,j],v[i]+memo[i-1][j-w[j]]);/
//将第i个物器发入背包
}
return memo[n-1][C];
}
};
0-1背包问题的优化1
以上代码的空间和时间复杂度均为O(n*C)
,其空间复杂度可以进一步优化
通过两行轮流,便可以实现,代码如下
#include<vector>
using namespace std;
class Knapsacko1 {
private:
public:
int knapsack01(const vector<int>& w,const vector<int>& v,int C) {
assert()
int n=w.size();
if(n=0)
return 0;
memo=vector<vector<int>(2,vector<int>(C+1,-1));
for(int j=0;j<=C;j++)
memo[0][j]=(j>=w[0]?v[0]:0);
for(int i=1;i<n;i++)
for(int j=0;j<=C;j++)
{ memo[i%2][j]=memo[(i-1)%2][j];//第一个策略,不放w[i]
if(j>=w[j])
memo[i%2][j]=max(memo[i%2,j],v[i]+memo[(i-1)%2][j-w[j]]);/
//将第i个物器发入背包
}
return memo[(n-1)%2][C];
}
};
0-1背包问题的空间优化2
那么我们能否使用一行大小为C的一维数组实现动规化呢?
实际上我们在考虑第i 个物品能否放入容量为c 的容器中时,只与上一行左边的元素有关,而于右边的无关。因此如果是使用一维数组更新的话,为了保证右边的格子的正确性,我们应该从右往左刷新格子。
对于1号与0号容量小于2 号,无法盛下,故可以提前结束程序。
#include<vector>
using namespace std;
class Knapsacko1 {
private:
public:
int knapsack01(const vector<int>& w,const vector<int>& v,int C) {
assert()
int n=w.size();
if(n=0)
return 0;
vector<int>memo(C+1,-1);
for(int j=0;j<=C;j++)
memo[j]=(j>=w[0]?v[0]:0);
for(int i=1;i<n;i++)
for(int j=C;j>=w[j];j--)
{
memo[j]=max(memo[j],v[i]+memo[j-w[j]]);
//将第i个物器发入背包
}
return memo[C];
}
};
0-1背包问题的变种
- 完全背包问题:每个物品可以无限使用转化为有限个物品的使用,只不过物品序列中有重复的,此外可以进一步优化
- 多重背包问题:每个物品不止一个有
nums[i]
个 - 多维费用背包问题:要考虑科技二路品的体积与重量。
- 物品间相互约束关系:依赖问题、排拆问题