背包问题(Knapsack Problem)
问题描述
一个旅行者随身携带一个背包,可以放入背包的物品有 n 种,每种物品的重量和价值分别为wi,vi。如果背包的最大重量限制是 b, 每种物品可以放多个。怎样选择放入背包的物品以使得背包的价值最大? 设上述wi,vi,b 都是正整数。
问题建模
解是 < x1, x2,…, xn >,其中 xi 是装入背包的第 i 种物品个数。
线性规划问题:由线性条件约束的线性函数取最大或最小的问题。
整数规划问题:线性规划问题的变量 xi 都是非负整数。
子问题界定和计算顺序
子问题界定:由参数 k 和 y 界定
k:考虑对物品 1, 2, … , k 的选择。
y:背包总重量不超过 y。
原始输入:k = n,y = b。
子问题计算顺序:k = 1,2, … , n 对于给定的 k,y = 1, 2, … ,b.
优化函数的递推方程
Fk(y):装前 k 种物品,总重不超过 y,背包达到的最大价值。
标记函数
ik(y):装前k种物品,总重不超y,背包达到最大价值时装入物品的最大标号
实例
追踪解
时间复杂度
备忘录需计算nb项,每项常数时间,计算时间为O(nb) .
代码实例
题目描述:
有N件物品和一个容量为V的背包。第i件物品的价值是C[i],重量是W[i]。求解将哪些物品装入背包可使价值总和最大。
输入描述:
输入第一行数 N V (1 <=N <=500) (1<= V <= 10000)
输入 N 行 两个数字 代表 C W (1 <= C <= 50000, 1 <= W <=10000)
示例
输入
5 10
8 6
10 4
4 2
5 4
5 3
输出
19
代码实现
#include <iostream>
#include <vector>
using namespace std;
int main()
{
int N,V;
while(cin >> N >> V)
{
vector<int> value(N);//存储每个物品的价值
vector<int> capacity(N);//存储每个物品的容量
for(int i = 0; i < N; ++i)
{
cin >> value[i] >> capacity[i];
}
vector<vector<int>> dp(N+1,vector<int>(V+1,0));
//有N+1行,但是从1开始遍历,所以每行表示每个物品
//有V+1列,但是从1开始遍历,所以每列表示从1开始到最大容量 的 各种情况下 的 物品最大价值存储
for(int i = 1; i < N+1; ++i)
{
for(int j = 1; j < V+1; ++j)
{
if(capacity[i-1] > j)//如果不下,那就等于上次的最优存储
{//这里的capacity[i-1]是因为这里的i从1开始
dp[i][j] = dp[i-1][j];
}
else//如果能放下,有两种情况:1、放 2、不放
//放和不放取决于放了之后是否是最优的,于是创建一个临时变量。
{//dp[i-1][j-capacity[i-1]]:i-1:上面一行,j-capacity[i-1]:装了i-1这个物品之后还剩的容量。
//所以整体就是:当前的tmp_best == 装了i-1物品的价值 + 装了这个物品后剩余的容量还可以装的最优的方案
int tmp_best = value[i-1] + dp[i-1][j-capacity[i-1]];
dp[i][j] = max(tmp_best,dp[i-1][j]);
}
}
}
//返回最后一个元素就是最优的方案
cout << dp[N][V] << endl;
}
return 0;
}