---
# System prepended metadata

title: DP
tags: [algorithm]

---

---
tags: algorithm
---
# DP

## 背包問題

來看到`DP`的一個經典例題，背包問題。
#### 問題簡述
給予一個容量為`k`的背包。
和一些物品，每個物品都有其大小和價值。
問再不超過背包容量的條件下，能裝入的最大價值。

### 0/1背包
每項物品都只有一個

背包容量為`M`,有`N`個物品。
$v[i] \implies 第i個物品的價值$
$w[i] \implies 第i個物品的重量$

#### DP解
```cpp= 
vector<int> v(N), w(N);
vector<vector<int>> dp(N, vector<int>(M + 1, 0));

for(int i = 0; i <　N; i++) {
    for(int j = 0; j < w[i]; j++) 
        dp[i][j] = dp[i - 1][j];
    for(int j = w[i]; j <= M; j++)
        dp[i][j] = max(dp[i - 1][j], v[i] + dp[i - 1][j - w[i]]);
}
```

#### 滾動DP

```cpp=
vector<int> v(N), w(N);
vector<int> dp(M + 1, 0);

for(int i = 0; i < N ;i++) {
    for(int j = M; j >= w[i]; j--)
        dp[j] = max(dp[j], v[i] + dp[j - w[i]]);
}
```
:::info
Q: 為什麼要從後往前DP呢?
:::


### 無限背包

從前往後`DP`, 即可。
```cpp=
vector<int> v(N), w(N);
vector<int> dp(M + 1, 0);

for(int i = 0; i < N ;i++) {
    for(int j = w[i]; j <= M; j++)
        dp[j] = max(dp[j], v[i] + dp[j - w[i]]);
}
```


---

### 有限背包
`DP，位元運算`

#### 一開始的想法
把每一個物品都做一次`O/1背包`。

```cpp=
vector<int> v(N), w(N), num(N);
for(int i = 0; i < N;i ++) 
    for(int t = 0; t < num[i]; t++) 
        for(int j = M; j >= w[i]; j--) 
            dp[j] = max(dp[j], v[i] +　dp[j - w[i]])；
```

#### 優化
把物品的數量拆成二的倍數。
```cpp= 
vector<int> v(N), w(N), num(N);
for(int i = 0; i < N; i++) {
    int rest = num[i];
    int weight, val;
    
    for(int t = 0; t <= rest; t <<= 1) { //t *= 2
        rest -= t;        
        weight = t * w[i], val = t * val[i];
        
        for(int j = M;j >= weight; j--)
            dp[j] = max(dp[j], val + dp[j - weight]);
    }
    
    
    weight = t * w[i], val = t * v[i];
    
    for(int j = M; j >=; j++) {
        dp[j] = max(dp[j], val + dp[j - weight]);
    }
}
```



