问题描述
问题分析
该问题能否用动态规划来解决,先来看一下是否具有最优子结构(后免得最优解能否用到前面的最优解答案):
- 物品种类:n (假设每种物品只有一个)
- 购物车可容纳重量:W
- 每个物品重量:w[i]
- 每个物品价值:v[i]
①将购物车分成两部分:可使用容量(Wa)和未使用容量(Wb)
②逐步增加(+1)Wa,直到达到Wa=购物车总容量:W(Wb=0)
③逐步增加物品的种类w[i] ,判断Wa当中是否有剩余容量放入该物品,(这里是体现最优子结构的地方,也是最难理解的地方)
i表示行数,代表物品种类数
j表示列数,代表购物车可用容量
c[i][j]表示在容量为j,物品数为i的情况下,当前能装入的最大价值是多少
距离来说明这两种情况:
c[i][j]=c[i-1][j] ,j<w[i]:这个的意思是,Wa=2,目前已经放入w[0]=1,v[i]=1的物品,现在还有一个w[1]=5,v[i]=5的物品,但是放不进,所以c[i][j]=c[i-1][j]=1。
下面这种情况就要做一个比较判断,当前容量可以塞进一个新物品,但是塞进这个物品可能会拿出其他的物品,所以要判断拿上这个物品总价值高,还是不拿高。
举例说明:
当前容量为2,
已装入重量为w[0]=1,价值为v[0]=2的物品,c[1][1]=2,
注意c[1][2]=2,因为只有这一种物品,而且只有一个
此时有新物品可以选择,他的重量为w[1]=2:
如果v[1]=1,那么我们应该只装入w[0]=1的物品,这样价值是最大的,即c[2][2]=c[1][2]=2;
如果v[1]=5,那么就可以毫不留情的抛弃掉w[0],直接装入w[1],即c[2][2]=c[i-1][j-w[i]]+v[i]=c[1][0]+5=5
注意初始化的时候要多加一行一列,并且赋值为0:c[0][j]=0:没有物品,所以总价值为0,c[i][0]=0:没有容量,所以总价值为0
算法设计
代码
int knapsack(int W, int N, vector<int>& wt, vector<int>& val) {
// vector 全填入 0,base case 已初始化
vector<vector<int>> dp(N + 1, vector<int>(W + 1, 0));
for (int i = 1; i <= N; i++) {
for (int w = 1; w <= W; w++) {
if (w - wt[i-1] < 0) {
// 当前背包容量装不下,只能选择不装入背包
dp[i][w] = dp[i - 1][w];
} else {
// 装入或者不装入背包,择优
dp[i][w] = max(dp[i - 1][w - wt[i-1]] + val[i-1],
dp[i - 1][w]);
}
}
}
return dp[N][W];
}
总结
0-1背包问题,无非就是状态转移+如何选择的问题,只要能找到递推公式,就比较好办了。