1. 問題描述
-
定義
揹包問題(來自於百度百科)
有N件物品和一個容量爲V的揹包。第i件物品的重量是w[i],價值是v[i]。求解將哪些物品裝入揹包可使這些物品的重量總和不超過揹包容量,且價值總和最大。
-
分析
- 我們要獲取這個問題的最優解, 也就是最佳的價值
- 可以使用貪婪算法對其進行一個遍歷
- 在遍歷時, 每當遇到一個物品的時候, 可分爲裝這個東西, 還是不裝這個東西.
- 遍歷時, 我們可以將這個問題產生的結果構成一顆二叉樹, 因爲每當遇到一個物品的時候,我們總是能將其分解成, 裝這個物品, 還是不裝這個物品.
- 當我們遍歷完成後, 所產生的最優解一定是最後的葉子結點中的一個.
2. 計算方法
-
DP計算公式
- 對上述公式的說明
- B(k, w) 表示的是我們的揹包問題的最優解, 其中k, 表示第k個物品, w表示揹包裏面還能裝的下的重量.
- B(k - 1, w - wk) + vk 的意思就是, 如果裝的話, 將這個第k個物品的重量(wk) 裝上, 加上這個物品的一個價值, 此時揹包中還能裝下的質量爲w - wk
- 如果不裝的話, 此時揹包裏面就剩餘w個重量
- 對上述公式的說明
-
最優解的表格, 我們以下面這個例子來說明
重量 2 3 4 5 9 價值 3 4 5 8 10 編號 1 2 3 4 5 揹包容量 20 -
對於以上這個例子, 我們可以得到下面這個表格
- 對於以上這個表格
- 其中行表示的是物品的編號, 列表示的是揹包所佔有的容量
- 每一個格子中的數字表示最大的數字, 就是我們的最優解
- 比如行爲3, 列爲5的時候, 格子中表示的數字就是, 只佔用5個揹包容量, 只裝前三個物品, 能夠得到的最大的價值
- 那麼對於整一個問題的最優解就是我們在所有的物品裏面做出選擇, 利用所有的揹包容量來裝, 得到的最大的價值, 就是表格的最右下角的那個格子的值
- 對於以上這個表格
-
下面我們就用代碼來求解一下上述的例子
3. 代碼
下面使用c語言實現一下
#include <stdio.h>
#include <stdlib.h>
#define N 6 // rows is 6
#define W 21 // column is 21
// global varables
int Bs[N][W] = {0}; // initialize the table which can show our max value
int weight[6] = {0, 2, 3, 4, 5, 9}; // weight of every goods
int values[6] = {0, 3, 4, 5, 8, 10}; // value of every goods
// calculate max value and fill the table
void knapsack() {
int k, C;// c is capacity, k is number of goods
for (k = 1; k < N; k++) {
for (C = 1; C < W; C++) {
// cannot load
if (weight[k] > C) {
Bs[k][C] = Bs[k - 1][C];
}
else {
// decide which is better if we load current goods
int value1 = Bs[k - 1][C - weight[k]] + values[k];
int value2 = Bs[k - 1][C];
if (value1 > value2) {
Bs[k][C] = value1;
}
else {
// not load
Bs[k][C] = value2;
}
}
}
}
}
// show the max value table
void printBs() {
for (int i = 0; i < N; i++) {
for (int j = 0; j < W; j++) {
printf("%2d ", Bs[i][j]);
}
printf("\n");
}
}
int main(int argc, char const *argv[])
{
knapsack();
// show the max values
printf("The max value is %d\n", Bs[5][20]);
printf("The max values table is the following \n");
printBs();
return 0;
}
4. 總結
- 以上代碼雖然能夠求得一個最優解, 最大值, 但是我們並不能直觀的展示具體裝了那個物品.
- 這個方法, 使用了一個一個遍歷, 對於每一個物品以及每一個容量都進行了遍歷迭代, 複雜度較高
- 會再繼續學習, 取得更好的解法