揹包問題(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;
}