|
1 | 1 | ---
|
2 | 2 | id: dp-bitmasks
|
3 | 3 | title: 'Bitmask DP'
|
4 |
| -author: Michael Cao, Siyong Huang |
| 4 | +author: Michael Cao, Siyong Huang, Peng Bai |
5 | 5 | contributors: Andrew Wang, Neo Wang
|
6 | 6 | prerequisites:
|
7 | 7 | - intro-bitwise
|
|
72 | 72 |
|
73 | 73 | where $S \setminus \{i\}$ is the subset $S$ without city $i$.
|
74 | 74 |
|
| 75 | +**Time Complexity:** $\mathcal{O}(2^N \cdot N^2)$ |
75 | 76 | <LanguageSection>
|
76 | 77 |
|
77 | 78 | <CPPSection>
|
@@ -173,7 +174,117 @@ print(dp[(1 << n) - 1][n - 1])
|
173 | 174 | ```
|
174 | 175 |
|
175 | 176 | </PySection>
|
| 177 | +</LanguageSection> |
| 178 | + |
| 179 | +## Merging Subsets |
| 180 | + |
| 181 | +In some problems, for a set $S$, it is not sufficient to transition from $S \setminus \{i\}$. |
| 182 | +Instead, it is necessary to transition from all *strict* subsets of $S$. |
| 183 | + |
| 184 | +Though it may seem like we have to do $\mathcal{O}(2^N \cdot 2^N) = \mathcal{O}(4^N)$ transitions, |
| 185 | +theres really only $\mathcal{O}(3^N)$ transitions! |
| 186 | + |
| 187 | +To see why, let's count the number of ordered pairs $(T, S)$ where $T \subset S$. |
| 188 | +Instead of counting directly, notice that each element $x$ is either: |
| 189 | +1. In $T$ and $S$ |
| 190 | +2. In neither |
| 191 | +3. In $S$ but not in $T$. |
| 192 | +If $x$ is in $T$ but not in $S$, $T$ isn't a valid subset. |
| 193 | + |
| 194 | +Given that each element can be in three possible states, our overall complexity is actually $\mathcal{O}(3^N)$. |
| 195 | + |
| 196 | +To implement this, we can do some bitwise tricks: |
| 197 | +<LanguageSection> |
| 198 | +<CPPSection> |
| 199 | + |
| 200 | +```cpp |
| 201 | +for (int mask = 0; mask < (1 << n); mask++) { |
| 202 | + for (int submask = mask; submask != 0; submask = (submask - 1) & mask) { |
| 203 | + int subset = mask ^ submask; |
| 204 | + // do whatever you need to do here |
| 205 | + } |
| 206 | +} |
| 207 | +``` |
| 208 | + |
| 209 | +</CPPSection> |
| 210 | +</LanguageSection> |
| 211 | + |
| 212 | +When we subtract $1$ from $\texttt{submask}$, the rightmost bit flips to a $0$ and all bits to the right of it will become $1$. |
| 213 | +Applying the bitwise AND with $\texttt{mask}$ removes all extra bits not in $\texttt{mask}$. |
| 214 | +From this process, we can get all strict subsets in increasing order by calculating $\texttt{mask} \oplus \texttt{submask}$, which does set subtraction. |
| 215 | + |
| 216 | +<FocusProblem problem="sam2" /> |
| 217 | + |
| 218 | +### Explanation |
| 219 | + |
| 220 | +The goal of this problem is to partition the nodes into sets such that the nodes in each set form a complete graph. |
| 221 | +Let $\texttt{dp}[S]$ be the minimum number of partitions such that in each partition, the graph formed is a complete graph. |
| 222 | + |
| 223 | +We can first find which sets $T$ form a complete graph, setting $\texttt{dp}[T]$ to $1$ and $\infty$ otherwise. |
| 224 | +This can be done naively in $\mathcal{O}(2^N \cdot N^2)$ or $\mathcal{O}(2^N \cdot N)$ |
| 225 | +by setting the adjacency list as a bitmask and using bit manipulations if a set of nodes is a complete graph. |
| 226 | + |
| 227 | +Then we can transition as follows: |
| 228 | +$$ |
| 229 | +\texttt{dp}[S] = \min_{T \subset S} (\texttt{dp}[T] + \texttt{dp}[S \setminus T]) |
| 230 | +$$ |
| 231 | + |
| 232 | +### Implementation |
176 | 233 |
|
| 234 | +**Time Complexity:** $\mathcal{O}(3^N + 2^N \cdot N)$ |
| 235 | + |
| 236 | +<LanguageSection> |
| 237 | +<CPPSection> |
| 238 | + |
| 239 | +```cpp |
| 240 | +#include <bits/stdc++.h> |
| 241 | +using namespace std; |
| 242 | + |
| 243 | +int main() { |
| 244 | + int n, m; |
| 245 | + cin >> n >> m; |
| 246 | + vector<int> adj(n); |
| 247 | + for (int i = 0; i < m; i++) { |
| 248 | + int u, v; |
| 249 | + cin >> u >> v; |
| 250 | + u--; |
| 251 | + v--; |
| 252 | + // adjacency list represented as a bitmask |
| 253 | + adj[u] |= (1 << v); |
| 254 | + adj[v] |= (1 << u); |
| 255 | + } |
| 256 | + |
| 257 | + vector<int> dp(1 << n, INT32_MAX); |
| 258 | + for (int mask = 0; mask < (1 << n); mask++) { |
| 259 | + bool connected = true; |
| 260 | + for (int u = 0; u < n; u++) { |
| 261 | + if (((mask >> u) & 1) != 0) { |
| 262 | + // check if u is connected to all other nodes in mask |
| 263 | + if (((adj[u] | (1 << u)) & mask) != mask) { |
| 264 | + connected = false; |
| 265 | + break; |
| 266 | + } |
| 267 | + } |
| 268 | + } |
| 269 | + |
| 270 | + if (connected) { dp[mask] = 1; } |
| 271 | + } |
| 272 | + |
| 273 | + for (int mask = 0; mask < (1 << n); mask++) { |
| 274 | + for (int submask = mask; submask; submask = (submask - 1) & mask) { |
| 275 | + int subset = mask ^ submask; |
| 276 | + // submask has everything in mask but not in subset |
| 277 | + if (dp[subset] != INT32_MAX && dp[submask] != INT32_MAX) { |
| 278 | + dp[mask] = min(dp[mask], dp[subset] + dp[submask]); |
| 279 | + } |
| 280 | + } |
| 281 | + } |
| 282 | + |
| 283 | + cout << dp[(1 << n) - 1] << endl; |
| 284 | +} |
| 285 | +``` |
| 286 | +
|
| 287 | +</CPPSection> |
177 | 288 | </LanguageSection>
|
178 | 289 |
|
179 | 290 | ### Problems
|
|
0 commit comments