给定背包容量C=9,给定四个物品与价值,能装入的最大价值是多少?
物品重量w = { 2,3,4,5 };
物品价值v = { 3,4,6,6 };
dp[i][j]表示对前i种物品,背包容量为j能装入的最大价值。
对于第i+1个物品,可以选择装或者不装两种情况
int backpack(vector<int> w, vector<int> v) {
int c = 0;
cin >> c;
int n = w.size();
vector<vector<int>>dp(n, vector<int>(c+1, 0));
for (int i = 1; i < n; i++) {
for (int j = 0; j <= c; j++) {
if (j - w[i] >= 0) {
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);
}
else {
dp[i][j] = dp[i - 1][j];
}
}
}
return dp[n - 1][c];
}
完全背包问题
每个物品可以多次选择。
1.贪心算法,计算v[i]/w[i],尽可能多的选择性价比高的物品。
2.动态规划,考虑每个物品选择i个的最优方案
算法思想:用数组dp[i][j]表示考虑前i个物品,背包容量为j情况下所能装下的最大价值量,dp[i][j] = max(dp[i - 1][j], dp[i - 1][j-kv[i-1]] + kw[i-1]),0=<kv[i-1] <= j。
k=0时表示装不下第i个物品或者不装第i个物品的情况,而dp[i - 1][j-kv[i-1]] + k*w[i-1]表示能装下第i个物品时,尝试所有可能.
最终结果返回dp[n][c],n表示物品个数,c表示背包容量。
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
int backpack(vector<int>v, vector<int>w, int c) {
int n = v.size();//物品数量
vector<vector<int>> dp(n+1, vector<int>(c + 1, 0));//dp[i][j]考虑前i个物品容量为c的背包能装下的最大价值
for (int i = 1; i <= n; i++) {//边界问题:i从1开始表示前i个,但是w和v下标都要-1
for (int j = 1; j <= c; j++) {
for (int k = 0; k*v[i-1] <= j; k++) {//k=0时表示装不下的可能
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j-k*v[i-1]] + k*w[i-1]);
}
}
}
return dp[n][c];
}
int main() {
vector<int> v = { 1,2,2,3,4 };
vector<int> w = { 1,2,3,5,7 };
cout<<backpack(v, w, 8);
system("Pause");
}
还有一种写法我非常推荐的
int backpack(int n, int c, vector<int> w, vector<int> v) {
vector<vector<int>> dp(n+1, vector<int>(c+1, 0));
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= c; j++) {
if (j - v[i-1] >= 0) {
dp[i][j] = max(dp[i-1][j], dp[i][j - v[i-1]] + w[i-1]);
}
else {
dp[i][j] = dp[i - 1][j];
}
}
}
return dp[n][c];
}
这里和01背包的代码区别仅仅是dp[i][j] = max(dp[i-1][j], dp[i][j - v[i-1]] + w[i-1]);
如果装的下的情况下,如果选择装,则在前i件都考虑的情况下的最大值再去装1个第件物品,来达到重复的效果
后记
在我学会凸优化后,我尝试使用线性规划解决01背包问题,结果是可以解决的,但是在这个case里面消耗的时间是DP的1000倍左右。
from pulp import *
import time
v = [3, 4, 5, 6]
w = [2, 3, 4, 5]
c = 8
n = 4
def knapsack_lp(n, v, w, c):
# 定义问题
prob = LpProblem("0/1 Knapsack Problem", LpMaximize)
# 定义决策变量
x = LpVariable.dicts("x", list(range(1, n + 1)), cat=LpBinary)
# 定义目标函数
prob += lpSum([v[i - 1] * x[i] for i in range(1, n + 1)])
# 定义约束条件
prob += lpSum([x[i] for i in range(1, n + 1)]) <= n # 每个物品最多只能选1次
prob += lpSum([w[i - 1] * x[i] for i in range(1, n + 1)]) <= c
# 求解问题
prob.solve()
# 输出结果
result = []
for i in range(1, n + 1):
result.append(x[i].varValue)
return value(prob.objective), result
def knapsack_dp(values, weights, capacity):
n = len(values)
# 初始化动态规划表
dp = [[0 for _ in range(capacity + 1)] for _ in range(n + 1)]
# 填充动态规划表
for i in range(1, n + 1):
for j in range(1, capacity + 1):
if weights[i - 1] <= j:
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weights[i - 1]] + values[i - 1])
else:
dp[i][j] = dp[i - 1][j]
# 回溯解
selected_items = []
i, j = n, capacity
while i > 0 and j > 0:
if dp[i][j] != dp[i - 1][j]:
selected_items.append(i - 1)
j -= weights[i - 1]
i -= 1
selected_items.reverse()
# 返回结果
return dp[n][capacity], selected_items
start_time = time.time()
knapsack_lp(n, v, w, c)
end_time = time.time()
lp_time = end_time - start_time
print("lp Elapsed time:", lp_time)
start_time = time.time()
knapsack_dp(v, w, c)
end_time = time.time()
dp_time= end_time - start_time
print("dp Elapsed time:", dp_time)
a = 100 / lp_time
dp_time = a * dp_time
print("lp", 100," ; ", "dp", dp_time)