# 110 選手班 - 動態規劃
###### tags: `宜中資訊` `CP`
Ccucumber12
2021.08.17
---
## Outline
- Knapsack
- 滾動陣列
- 狀態壓縮
- Sum-over-Subset
- Optimization
---
## Knapsack
----
### 0-1 Knapsack - 1
- 給定 $N$ 個物品,第 $i$ 個物品重量$w_i$,價值$v_i$
- 背包容量 $W$
- 選擇一些物品,使得重量總和不超過容量,請問最大價值總和為何?
- $N \leq 100$,$W \leq 10^5$
----
每個物品
- 0: 不選,重量$+0$,價值$+0$
- 1: 選擇,容量$-w_i$,價值$+v_i$
----
- $\text{dp}[i][j]:=$只能選前 $i$ 個物品的情況下,背包容量為 $j$ 的最大價值總和。
- 考慮第 $i$ 個物品
- 0: $\text{dp}[i][j] = \text{dp}[i-1][j]$
- 1: $\text{dp}[i][j] = \text{dp}[i-1][j-w_i]+v_i$
- Ans: $\text{dp}[N][W]$
----
Complexity
- Time: $\mathcal{O}(NW)$
- Space: $\mathcal{O}(NW)$
----
### 0-1 Knapsack - 2
- 給定 $N$ 個物品,第 $i$ 個物品重量$w_i$,價值$v_i$
- 背包容量 $W$
- 選擇一些物品,使得重量總和不超過容量,請問最大價值總和為何?
- $N \leq 100,W \leq 10^9,\sum v_i \leq 10^5$
----
- $\text{dp}[i][j]:=$只能選前 $i$ 個物品的情況下,價值總合為 $j$ 的最小重量總和。
- 考慮第 $i$ 個物品
- 0: $\text{dp}[i][j] = \text{dp}[i-1][j]$
- 1: $\text{dp}[i][j] = \text{dp}[i-1][j-v_i]+w_i$
----
Answer
- For every $\text{dp}[i][j] \leq W$
- Max $j$
----
Initialize
- $\text{dp}[i][0] = 0$
- $\text{dp}[i][j] = \inf, (j > 0)$
----
Complexity
- Time: $\mathcal{O}(N\sum v_i)$
- Space: $\mathcal{O}(N\sum v_i)$
----
### 0-1 Knapsack - 4
- 給定 $N$ 個物品,第 $i$ 個物品重量$w_i$,價值$v_i$
- 背包容量 $W$
- 選擇一些物品,使得重量總和不超過容量,請問最大價值總和為何?
- $N \leq 20,W \leq 10^9$
----
Enumeration
- $\mathcal{O}(2^N)$
----
### 0-1 Knapsack - 5
- 給定 $N$ 個物品,第 $i$ 個物品重量$w_i$,價值$v_i$
- 背包容量 $W$
- 選擇一些物品,使得重量總和不超過容量,請問最大價值總和為何?
- $N \leq 40,W \leq 10^9$
----
Enumeration
- $\mathcal{O}(2^N)$ ?
----
折半枚舉
- set $A$:item $[1,\ 20]$
- set $B$:item $[21,\ 40]$
- combine $A$ and $B$
----
Combine
- subset of $A$:$a\ (w_a,\ v_a)$
- for all subset of $B$:$b$
- $w_b \leq W - w_a$
- Find max $v_b$
- Sort + Two pointers
----
Time Complexity
- enumerate $A$: $\mathcal{O}(2^\frac{N}{2})$
- enumerate $B$: $\mathcal{O}(2^\frac{N}{2})$
- Combine:
- Sort: $\mathcal{O}(2^\frac{N}{2}\log 2^\frac{N}{2})=O(N \times 2^\frac{N}{2})$
- Two pointers: $\mathcal{O}(2^\frac{N}{2})$
- Total: $\mathcal{O}(N \times 2^\frac{N}{2})$
----
### Unbounded Knapsack
- 給定 $N$ 種物品,第 $i$ 種物品重量$w_i$,價值$v_i$
- 背包容量 $W$
- 選擇任意數量物品,每種不限個數,使得重量總和不超過容量,請問最大價值總和為何?
- $N \leq 100,W \leq 10^5$
----
- $\text{dp}[i][j]:=$只能選前 $i$ 個物品的情況下,背包容量為 $j$ 的最大價值總和。
- 考慮第 $i$ 個物品
- 0: $\text{dp}[i][j] = \text{dp}[i-1][j]$
- 1: $\text{dp}[i][j] = \text{dp}[i-1][j-w_i]+v_i$
- 2: $\text{dp}[i][j] = \text{dp}[i-1][j-2w_i]+2v_i$
- 3: $\text{dp}[i][j] = \text{dp}[i-1][j-3w_i]+3v_i$
- $\dots$
----
- $\text{dp}[i][j] = \text{dp}[i-1][j - w_i] + v_i$
- 每項物品只能選一次
- 01背包
- $\text{dp}[i][j] = \text{dp}[i][j - w_i] + v_i$
- 每項物品選完之後還能選
- 無限背包
----
- $\text{dp}[i][j]:=$只能選前 $i$ 個物品的情況下,背包容量為 $j$ 的最大價值總和。
- 考慮第 $i$ 個物品
- 0: $\text{dp}[i][j] = \text{dp}[i-1][j]$
- 1: $\text{dp}[i][j] = \text{dp}[i][j-w_i]+v_i$
----
Complexity
- Time: $\mathcal{O}(NW)$
- Space: $\mathcal{O}(NW)$
----
### Bounded Knapsack
- 給定 $N$ 種物品,第 $i$ 種物品重量$w_i$,價值$v_i$,共有$k_i$個
- 背包容量 $W$
- 選擇任意數量物品,使得重量總和不超過容量,請問最大價值總和為何?
- $N \leq 100,W \leq 10^5$
----
- $\text{dp}[i][j]:=$只能選前 $i$ 個物品的情況下,背包容量為 $j$ 的最大價值總和。
- 考慮第 $i$ 個物品
- 0: $\text{dp}[i][j] = \text{dp}[i-1][j]$
- 1: $\text{dp}[i][j] = \text{dp}[i-1][j-w_i]+v_i$
- 2: $\text{dp}[i][j] = \text{dp}[i-1][j-2w_i]+2v_i$
- $\dots$
- $k_i$: $\text{dp}[i][j] = \text{dp}[i-1][j-k_iw_i]+k_iv_i$
----
#### 砝碼問題
想要製造出 $1 \sim 100$ 之間任意整數的重量,請問最少需要多少個砝碼?
----
- 二進位
- $1, 2, 4, 8, 16, 32, 37$
- Ans: $\lceil \log_2 100 \rceil$
----
- 想要製造出 $1 \sim k$ 之間任意整數的數量
- 二進位拆解成 $\log k$ 個
- 每個獨立選擇取或不取
- 0-1 背包問題
----
Complexity
Time: $\mathcal{O}(N\log kW)$
Space: $\mathcal{O}(N\log kW)$
----
### 混和背包
- 最多取一次
- 最多取 $k$ 次
- 可以取無限次
----
- 0-1: 最多取一次的有限背包
- 無限: 最多取 $\frac{W}{w_i}$ 次的有限背包
----
Solution
還是有限背包
----
### 二維背包
- 給定 $N$ 個物品,第$i$個物品價值$v_i$,但要消耗兩種代價$a_i$和$b_i$
- 背包兩種代價容量 $W$ 和 $U$
- 選擇一些物品,使得兩種代價總和皆不超過背包容量,請問最大價值總和為何?
- $N \leq 100$,$W,U \leq 10^3$
----
每個物品
- 0: 不選,容量$-0 / -0$,價值$+0$
- 1: 選擇,容量$-a_i / -b_i$,價值$+v_i$
----
- $\text{dp}[i][j][k]:=$只能選前 $i$ 個物品的情況下,背包容量為 $j$ 和 $k$ 的最大價值總和。
- 考慮第 $i$ 個物品
- 0: $\text{dp}[i][j][k] = \text{dp}[i-1][j][k]$
- 1: $\text{dp}[i][j][k] = \text{dp}[i-1][j-a_i][k-b_i]+v_i$
- Ans: $\text{dp}[N][W][U]$
----
Complexity
Time: $\mathcal{O}(NWU)$
Space: $\mathcal{O}(NWU)$
----
### 限量背包
- 給定 $N$ 個物品,第 $i$ 個物品重量$w_i$,價值$v_i$
- 背包容量 $W$
- 選擇至多 $M$ 個物品,使得重量總和不超過容量,請問最大價值總和為何?
- $N,M \leq 100$,$W \leq 10^5$
----
每個物品
- 0: 不選,容量$-0$,個數$-0$,價值$+0$
- 1: 選擇,容量$-w_i$,個數$-1$,價值$+v_i$
----
二維背包
- 第一個代價
- 物品:$w_i$
- 容量:$W$
- 第二個代價
- 物品:$1$
- 容量:$M$
----
Complexity
- Time: $\mathcal{O}(NMW)$
- Space: $\mathcal{O}(NMW)$
----
### 分組背包
- 給定 $N$ 個物品,第 $i$ 個物品重量$w_i$,價值$v_i$,屬於組別 $k_i$
- 背包容量 $W$,物品共分為 $K$ 組
- 每組最多選擇一件物品,使得重量總和不超過容量,請問最大價值總和為何?
- $N,K \leq 100$,$W \leq 10^5$
----
- $\text{dp}[i][j]:=$只能選前 $i$ **組**物品的情況下,背包容量為 $j$ 的最大價值總和。
- 考慮第 $i$ **組**物品
- 考慮第 $i$ 組物品內,第 $u$ 個物品
- 0: $\text{dp}[i][j] = \text{dp}[i-1][j]$
- 1: $\text{dp}[i][j] = \text{dp}[i-1][j-w_u]+v_u$
- Ans: $\text{dp}[K][W]$
----
Complexity
Time: $\mathcal{O}(NW)$
Space: $\mathcal{O}(NW)$
---
## 滾動陣列
----
### 0-1 Knapsack
- Space Complexity: $\mathcal{O}(NW)$
- MLE :sad:
- 減少不必要的地方
----
- $\text{dp}[i][j] = \max(\text{dp}[i-1][j], \text{dp}[i-1][j-w_i] + v_i)$
- $\text{dp}[j] = \max(\text{dp}[j], \text{dp}[j-w_i] + v_i)$
- $j$: $W \rightarrow 1$
- Space: $\mathcal{O}(W)$
----
### Unbounded Knapsack
- $\text{dp}[i][j] = \max(\text{dp}[i-1][j], \text{dp}[i][j-w_i] + v_i)$
- $\text{dp}[j] = \max(\text{dp}[j], \text{dp}[j - w_i] + v_i)$
- $j$: $1 \rightarrow W$
- Space: $\mathcal{O}(W)$
----
### 路徑規劃問題 - 1
- 給定一張 $N$ 點 $M$ 邊的帶權有向圖
- 請問從 $S$ 走到 $T$,洽經過 $K$ 條邊的最短路長度
- $N,M,K \leq 3000$,Memory: 1MB
----
- $\text{dp}[i][v]:=$從 $S$ 出發洽經過 $i$ 條邊到達 $v$ 的最短路長度
- $\text{dp}[i][v] = \displaystyle\min_{(u,v) \in G}(\text{dp}[i-1][u] + W_{uv})$
- Space Complexity: $\mathcal{O}(KN)$
- Time Complexity: $\mathcal{O}(KN)$
----
- $\text{dp}[0/1][v] = \displaystyle\min_{(u,v) \in G}(\text{dp}[1/0][u] + W_{uv})$
- $\text{dp}[i\& 1][v] = \displaystyle\min_{(u,v) \in G}(\text{dp}[i\&1 \verb=^= 1][u] + W_{uv})$
- Space Complexity: $\mathcal{O}(N)$
----
### 路徑規劃問題 - 2
- 給定一張 $N$ 點 $M$ 邊的帶權有向圖
- 請問從 $S$ 走到 $T$,洽經過 $K$ 條邊的最短路長度
- 請輸出解
- $N,M,K \leq 3000$,Memory: 1MB
----
滾動陣列
- Space Complexity: $\mathcal{O}(N)$
- 無法留存過程中的點
----
求第 $M$ 個點
- 多開一個陣列紀錄:第 $M$ 列與之後轉移點的關係
- Space Complexity: $\mathcal{O}(N)$
----
考慮分治
- 每次尋找中間的點為何
- 遞迴上下部分再次尋找中間點
----
- solve(L, R, dpL, dpR) = dpM
- L: 第 L 列,遞迴左界
- R: 第 R 列,遞迴右界
- dpL: 第 L 列的起始點
- dpR: 第 R 列的終止點
- dpM: 中間點的解
- 往下遞迴
- solve(L, M, dpL, dpM)
- solve(M, R, dpM, dpR)
----
- $T(K) = O(NK) + 2T(\frac{K}{2})$
- $T(K) = O(NK\log K)$
----
Complexity
- Time: $\mathcal{O}(NK\log K)$
- Space: $\mathcal{O}(N)$
- 以時間換取空間
---
## 狀態壓縮
----
### TIOJ 1014
- 有 $N$ 個地鼠,分別在數線上 $1$ 到 $N$ 的整數點上
- 地鼠 $i$ 每 $t_i$ 秒會出現一次
- 玩家從 $0$ 出發,每秒可以移動一格,打地鼠時間忽略不計,請問打完所有地鼠所需的最少秒數
- $N \leq 16$
----
- $N \leq 4$
- $\text{dp}[n][s_1][s_2][s_3][s_4] = S$
- $n$: 目前在地鼠 $n$ 的位置
- $s_i$: 是否打過地鼠 $i$ (0/1)
- $S$: 最少秒數
- $\text{dp}[2][0][1][1][1] = \min\dots$
- $\text{dp}[3][0][0][1][1] + f(t, 2)$
- $\text{dp}[4][0][0][1][1] + f(t, 2)$
- Ans: $\text{dp}[i][1][1][1][1]$
----
- $N \leq 16$
- $\text{dp}[n][s] = S$
- $s$: 二進位表示各個地鼠的狀況
- $\text{dp}[n][s] = \min(dp[i][s - (1<<i)] + f(t, n))$
- for all $i$ S.T. $(s \& (1<<i)) != 0$
- Ans: $\text{dp}[i][2^n-1]$
----
Complexity
- Time: $\mathcal{O}(N^2 \times 2^N)$
- Space: $\mathcal{O}(N \times 2^N)$
----
### Other
- Travelling Salesman Problem
- Hamilton Circuit
----
### 3 status
- $\text{dp}[s_1][s_2][s_3]...[s_n]$
- $s_i$: 三種狀態 (0 / 1 / 2)
- $\text{dp}[s]$
- $s$: 轉成三進位判斷第 $i$ 個的狀態
- Complexity: $\mathcal{O}(3^N)$
---
## Sum-over-Subset
----
- Sum-over-Subset DP
- SoS DP
- Möbius Transform
- 莫比烏斯轉換
----
### Problem
- $N$ 個元素形成各種子集 $S$
- 計算所有 $f(S) = \sum\limits_{S' \subseteq S}f(S')$
- 任意子集之值來自其子集之值
- 子集的子集
----
### Solution - 1
```c=
int subsetEnum(int S) {
int subset = S, ret = 0 ;
while(subset > 0) {
ret += f[subset] ;
subset = (subset - 1) & S ;
}
return ret ;
}
```
----
- 從 $\text{subset} = S$ 開始,由大到小枚舉所有子集
- 若 $\text{subset}$ 為奇數:$\text{subset}$ - 1
- 若 $\text{subset}$ 為偶數:最低位 1 變成 0,剩下的與原 $S$ 取一樣
----
Time Complexity
- 每個元素三種狀況:
- 0: 不在 $S$ 裡面
- 1: 在 $S$ 裡面,但是不在 $\text{subset}$ 裡面
- 2: 在 $S$ 裡面,也在 $\text{subset}$ 裡面
- $\mathcal{O}(3^N)$
----
### Solution - 2
- 將 $S$ 的子集依照 **二進位下與$S$的共同後綴長度** 分類
- $N = 3$,$S = 6 = 110_{(2)}$
- $L(S,3) = \{110_{(2)}\}$
- $L(S,2) = \{110_{(2)}, 010_{(2)}\}$
- $L(S,1) = \{110_{(2)}, 010_{(2)}, 100_{(2)}, 000_{(2)}\}$
- $L(S,0) = \{110_{(2)}, 010_{(2)}, 100_{(2)}, 000_{(2)}\}$
----
\begin{equation}
\left.
L(S,i)
\right.
=
\left.
\begin{cases}
{S}, & \text{if}\ i = N\\
L(S,i+1), & \text{if}\ i \notin S\\
L(S,i+1) \cup L(S / {i}, i+1), & \text{otherwise}
\end{cases}
\right.
\end{equation}
----
- 邊界條件:$L(S,N) = S$
- $S$ 第 $i$ 個 bit 為 $0$:直接取下一個bit
- $S$ 第 $i$ 個 bit 為 $1$:有含$i$ + 不含$i$
----
```c=
int dp[1 << N][N], S[N] ;
void SoSDP(int n) {
for(int i = 0; i < (1<<n); ++i) dp[i][n] = S[i] ;
for(int i = n - 1; i >= 0; --i) {
for(int s = 0; s < (1 << n); ++s) {
dp[s][i] = dp[s][i + 1] ;
if(s & (1<<i)) dp[s][i] += dp[s^(1<<i)][i + 1] ;
}
}
}
```
----
滾動陣列
```c=
int dp[1 << N], S[N] ;
void SoSDP(int n) {
for(int i = 0; i < (1<<n); ++i) dp[i][n] = S[i] ;
for(int i = n - 1; i >= 0; --i) {
for(int s = (1<<n)-1; s >= 0; --s) {
if(s & (1<<i)) dp[s] += dp[s^(1<<i)] ;
}
}
}
```
----
Complexity
- Time: $\mathcal{O}(N2^N)$
- Space: $\mathcal{O}(2^N)$
---
## Optimization
----
### 單調隊列優化
- 轉移點具有單調性
- 減少轉移點的尋找過程
- 均攤分析
----
### CF 372C
- $M$ 個煙火在數線上綻放,第 $i$ 個煙火在 $t_i$ 時,於 $a_i$ 綻放
- 若你在 $x$ 觀賞煙火 $i$,你會獲得 $b_i - |a_i - x|$ 的開心度
- 你每秒可以移動 $d$ 單位距離,並可以任意選初始位置
- 請問看完 $M$ 個煙火的最大開心度為何?
- $M \leq 300,1 \leq a_i \leq N,N \leq 1.5\times 10^5$
----
Order
- 依照時間順序觀賞煙火
- 將煙火依照 $t_i$ 排序
----
- $\text{dp}[i][j]:=$看完第 $i$ 個煙火,且位於 $j$ 的開心度
- $\text{dp}[i][j] = \displaystyle\max_{|k-j| \leq d \times (t_i - t_{i-1})}(\text{dp}[i-1][k] + b_i - |a_i - j| )$
- Time: $\mathcal{O}(MN^2)$
----
- $\text{dp}[i][j] = \displaystyle\max_{|k-j| \leq d \times (t_i - t_{i-1})}(\text{dp}[i-1][k]) + b_i - |a_i - j|$
- let $D = d \times (t_i - t_{i-1})$
- $\text{dp}[i][j] = \displaystyle\max_{j-D \leq k \leq j+D}(\text{dp}[i-1][k]) + b_i - |a_i - j|$
- Sliding Window
- Time: $\mathcal{O}(MN)$
----
### 分治優化
- 轉移點具有單調性
- 縮有轉移點可能範圍
- 分治想法
----
### CF 321E
- $N$ 個人排隊搭船,有 $K$ 艘船依次前來載人
- 第 $i$ 艘船來時,前面 $q_i$ 個人就會上船
- 給定第 $i$ 人與第 $j$ 人的陌生度 $u[i][j]$,只要兩人在同一艘船上,就會加上此陌生度
- 請分配 $q_i$ 使得陌生度總和最小
- $N \leq 4000,K \leq 800,1\leq u[i][j] \leq 9$
----
- $\text{dp}[i][j]:=$前 $j$ 個人已經用 $i$ 艘船載走的陌生度
- $\text{dp}[i][j] = \displaystyle\min_{k < j}(\text{dp}[i-1][k] + C(k, j))$
- $C(k,j):=$第 $k$ 到第 $j$ 個人在同一艘船上的陌生度總和
- Time: $\mathcal{O}(KN^2)$
----
- $C(k,j)$ 隨長度成長極快
- 盡量越平均越好
- 當 $j$ 增加時, $k$ 理當也要隨其增加
- ( 直覺 )
----
- 若 $\text{dp}[i][k]$ 的轉移點為 $x$
- $\text{dp}[i][k+1]$ 的轉移點 $\geq x$
- $\text{dp}[i][k-1]$ 的轉移點 $\leq x$
----
- 計算 $\text{dp}[i][1 \sim N]$
- 先計算 $\text{dp}[i][mid]$ 轉移點為 $x$
- 分治遞迴
- $\text{dp}[i][1 \sim mid]$ 轉移點 $[1 \sim x]$
- $\text{dp}[i][mid \sim N]$ 轉移點 $[x \sim N]$
- Time: $\mathcal{O}(KN\log N)$
----
### 斜率優化
- 轉移式為線性函數
- 所有轉移可能為一個 下/上 凸包
----
- $\text{dp}[i] = \displaystyle\max_{j \leq R_i}(a_jx_i + b_j) + c_i$
- $R_i$ 是遞增序列
- $a_jx_i + b_j$ 為線性函數
----
- $R_i$ 增加 $\rightarrow$ 插入新的直線
- 尋找最大值 $\rightarrow$ 找點 $x$ 的最大值
- 凸包二分搜
----
![](https://i.imgur.com/4RsxDp7.png)
----
- $\text{dp}[i] = \max\{(a_i - a_j)^2 + c_j\}$
- $\text{dp}[i] = \max\{a_i^2 - 2a_ia_j + a_j^2 + c_j\}$
- $\text{dp}[i] = \max\{(-2a_j)a_i + (a_j^2+c_j)\} + a_i^2$
- 斜率優化
---
## Credit
- OI wiki
- IONC
- IOIC
- http://www2.lssh.tp.edu.tw/~hlf/class-1/lang-c/DP.pdf
- https://codeforces.com/blog/entry/63823
----
### AtCoder DP X-Tower
- There are $N$ blocks, block $i$ has a weight of $w_i$, a solidness of $s_i$ and a value of $v_i$
- Choose some blocks to build a tower which follows the condition:
- For each block $i$, the sum of weights of blocks above it is not greater than $s_i$
- Find the maximum possible sum of value in the tower
- $N \leq 10^3,w_i,s_i \leq 10^4$
{"metaMigratedAt":"2023-06-16T07:36:39.228Z","metaMigratedFrom":"Content","title":"110 選手班 - 動態規劃","breaks":true,"contributors":"[{\"id\":\"45262441-89e4-46ca-959b-c0d6fadb64e2\",\"add\":13063,\"del\":459}]"}