---
# System prepended metadata

title: Chữa bài DP bitmask 12/07/2023
tags: [bitmasks, dp]

---

Các bạn nhớ xem lại các thao tác với bit, lấy bit, đảo bit.

## Bài 1: [Bảng nhị phân](https://marisaoj.com/problem/378)
- Công thức DP: $f(i, mask)$, xây đến cột thứ $i$, các giá trị điền vào cột thể hiện bởi $mask$.
- Lưu ý trong $mask$ không được có hai bit nào cạnh nhau:
- Với mỗi $mask$, tìm $mask_2$ nằm ở cột $i-1$, nếu $mask \text{ AND } mask_2=0$, nghĩa là $mask$ có thể nằm cạnh $mask_2$ do không có các bit $1$ kề nhau, cập nhật $f(i-1, mask_2)$ lên $f(i,mask)$
- Đáp án à $\sum{f(m,mask)}$ do hàng $m$ có thể là bất cứ $mask$ nào.

Code:
:::spoiler
```c++
#include <iostream>
#include <vector>

const int MOD = 1000000007;

int countValidBoards(int n, int m) {
    std::vector<std::vector<int>> dp(1 << m, std::vector<int>(n + 1));

    // Base case
    dp[0][0] = 1;

    for (int row = 1; row <= n; ++row) {
        for (int mask = 0; mask < (1 << m); ++mask) {
            for (int newMask = 0; newMask < (1 << m); ++newMask) {
                if ((newMask & (newMask << 1)) == 0 && (newMask & mask) == 0) {
                    dp[newMask][row] = (dp[newMask][row] + dp[mask][row - 1]) % MOD;
                }
            }
        }
    }

    int total = 0;
    for (int mask = 0; mask < (1 << m); ++mask) {
        total = (total + dp[mask][n]) % MOD;
    }

    return total;
}

int main() {
    int n, m;
    std::cin >> m >> n;

    int result = countValidBoards(n, m);
    std::cout << result << std::endl;

    return 0;
}
```
:::

## Bài 2: [Bài toán người bán hàng 2](https://marisaoj.com/problem/215)
- Công thức DP: $f(i, mask)$, hiện tại đang đứng ở vị trí $i$. Các bit $1$ trong $mask$ thể hiện các thành phố đã thăm.
- Đánh số các thành phố từ $0$ đến $n-1$, trạng thái bắt đầu $f(0,1)=0$, ở đây, ta bắt đầu từ thành phố $0$, đánh dấu mask là $...0001_2=1$ là đã thăm.
- Vỉ sao bắt đầu ở $0$, vì đề bài yêu cầu đi thành một chu trình khép kín, nên dù bắt đầu ở đâu thì vẫn phải đi thành một vòng.
- Với mỗi $f(i,mask)$, ta sẽ chọn một thành phố $j$ sao cho bit $j$ trong $mask$ là $0$, nghĩa là chưa được thăm, cập nhật $f(j,mask \text{ OR } 1 \ll j) = min(f(j,mask \text{ OR } 1 \ll j), f(i,mask)+A_{i,j})$.
- Đáp án là $f(x, 2^n-1) + A_{x,0}$ với $x$ là một TP khác $0$, phải quay về $0$ nên cộng thêm $A_{x,0}$.

- Code
:::spoiler
```c++
#include <bits/stdc++.h>
using namespace std;

const int maxn = 21, oo = 1e9; int n; int c[21][21]; int dp[(1<<21)][21];

signed main() {
    cin.tie(0); ios_base::sync_with_stdio(false);
    cin >> n;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            cin >> c[i][j];
        }
    }

    memset(dp, 0x3f, sizeof dp);
    dp[1][0]=0;

    for (int mask = 0; mask < (1 << n); mask++) {
        for (int i = 0; i < n; i++) {
            if (((mask >> i) & 1) == 0) continue;

            for (int j = 0; j < n; j++) {
                if (((mask >> j) & 1) == 1) continue;
                dp[mask | (1 << j)][j] = min(dp[mask | (1 << j)][j],
                                             dp[mask][i] + c[i][j]);
            }
        }
    }

    int res = 1e9;
    for (int i = 1; i < n; i++) {
        res = min(res, dp[(1 << n) - 1][i] + c[i][0]);
    }

    cout << res << '\n';
}
```
:::

## Bài 4: [Pha thuốc 5](https://marisaoj.com/problem/221)
- Công thức: $f(mask)$ là đã chọn những cây nấm thể hiện trong $mask$, sức mạnh tối đa là bao nhiêu. Base case $f(0)=0$, chưa chọn cây nấm nào nên sức mạnh là $0$.
- Với mỗi $f(mask)$, chọn ra $i$ và $j$ sao cho bit $i$ và $j$ trong mask là $0$, cập nhật $f(mask \text{ OR } 1 \ll i \text{ OR } 1 \ll j) = max(f(mask) + A_{i,j})$. Cho thêm cây nấm thứ $i$ và $j$ tạo thành một lọ thuốc.
- Đáp án là $f(2^{n\times2}-1)$, khi sử dụng hết các cây nấm.
- Code
:::spoiler
```c++
#include <bits/stdc++.h>

using namespace std;
#define ll int
#define fi first
#define sc second
const int inf = 1e8;
ll dp[1 << 20][20];
ll n;
ll a[20][20];
void inp() {
    cin >> n;
    for (int i = 0; i < 2 * n; i++) {
        for (int j = 0; j < 2 * n; j++) {
            cin >> a[i][j];
        }
    }
}

ll solve(ll x, ll mask) {
    if (x == n) return 0;
    if (dp[mask][x] != -1) return dp[mask][x];
    ll ans = -inf;

    for (int i = 0; i < 2 * n; i++) {
        if (!(mask & (1 << i))) {
            for (int j = 0; j < i; j++) {
                if (!(mask & (1 << j))) {
                    ans = max(ans, solve(x + 1, mask | (1 << i) | (1 << j)) + a[i][j]);
                }
            }
        }
    }
    return dp[mask][x] = ans;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    inp();
    
    memset(dp, -1, sizeof dp);
    
    cout << solve(0, 0);

}
```
:::