---
# System prepended metadata

title: 01 背包問題

---

# 01 背包問題

## 問題描述:

有n件物品和一個容量為W的背包。第i件物品的重量是w[i]，價值是v[i]。求解將哪些物品裝入背包可使價值總和最大。

## 解:
直接用例子說明:
5個物品,（重量,價值）分別為：（5,12）,（4,3）,（7,10）,（2,3）,（6,6）



| 背包容量 | 0   | 1   | 2   | 3   | 4   | 5   | 6   | 7   | 8   | 9   | 10  | 11  | 12  | 13  | 14  | 15  |
| -------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|   物品1+~5    | 0 | 0| 0 |0|0| 0| 6| 12 | 12| 15|15 | 18 | 22 | 22|25 | 25 |
|   物品1+~4    |0| 0 |3 | 3| 3 | 3 | 3| 12|12 |15 |15| 18 | 22 | 22 | 25 |25 |
|   物品1+2+3   | 0| 0 | 0| 0 |0 |0 |0 |12 | 12 | 15 | 15 | 15 |22 |  22| 22 |22 |
|   物品1+2     |0|0 | 0 | 0 | 3 |12 | 12|12 |12 | 15| 15| 15| 15 | 15 | 15 |15 |
|   物品1       |0|0|0|0|0|12|12|12|12|12|12|12|12|12|12|12|


```

for(int i = 1; i <= n; i++){
    for(int j = 0; j <= W; j++){
        if(j < w[i])    dp[i][j] = dp[i-1][j]; //沒辦法放，物品重量超過背包容量
        else dp[i][j] =  max(dp[i-1][j], dp[i-1][j - w[i]] + v[i]);
                      // max(不放物品i  , 放物品i);
    }
}
```

優化空間複雜度:
以上方法的時間和空間複雜度均為O(VN)，其中時間複雜度已經不能再優化了，但空間複雜度卻可以優化到O(V)。因為其實根本不需要存二維陣列，只有最上面那一列是重要的。例如:放入物品1,2,3之後，物品1,物品1+2這兩列便可捨棄。這種二維轉一維的儲存方式稱為滾動數組。

```
for(int i = 1; i <= n; i++){
    for(int j = W; j >= w[i]; j--)
        dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
             // max(不放i, 放i);
}
```

初始化:
若要求恰好裝滿背包，那麼在初始化時除了f[0]為0外其它f[1..V]均設為-∞，這樣就可以保證最終得到的f[N]是一種恰好裝滿背包的最優解。

如果並沒有要求必須把背包裝滿，而是只希望價格盡量大，初始化時應該將f[0..V]全部設為0。因為什麼都不放也是一種放法。

## NCTU 484-Seesaw
description: 給一串數字，將數字分成兩組，使兩組總和差距最小。求最小差距為何。
solve: 想像成欲填滿總和一半重量的背包。再將總和扣掉能填滿的最大重量*2(此題價值和重量視為相等)。

```
#include<bits/stdc++.h>

using namespace std;

int dp[10001];
int main(){
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    memset(dp,0,sizeof(dp));
    int n;
    cin>>n;
    int sum=0;
    vector<int> vec(n+1);
    for(int i=1;i<=n;++i){
        int x;
        cin>>x;
        sum+=x;
        vec[i]=x;
    }
    int target=sum/2;

    for(int i=1;i<=n;++i){
        for(int j=target;j>=vec[i];j--){
            dp[j]=max(dp[j],dp[j-vec[i]]+vec[i]);
        }
    }

    cout<<sum-dp[target]*2<<endl;




return 0;
}
```

## ps:
另有fractional背包問題，可直接用greedy解(從最大價值的開始拿)。





