G. Shuffling Songs

onlyblues發表於2024-04-01

G. Shuffling Songs

Vladislav has a playlist consisting of $n$ songs, numbered from $1$ to $n$. Song $i$ has genre $g_i$ and writer $w_i$. He wants to make a playlist in such a way that every pair of adjacent songs either have the same writer or are from the same genre (or both). He calls such a playlist exciting. Both $g_i$ and $w_i$ are strings of length no more than $10^4$.

It might not always be possible to make an exciting playlist using all the songs, so the shuffling process occurs in two steps. First, some amount (possibly zero) of the songs are removed, and then the remaining songs in the playlist are rearranged to make it exciting.

Since Vladislav doesn't like when songs get removed from his playlist, he wants the making playlist to perform as few removals as possible. Help him find the minimum number of removals that need to be performed in order to be able to rearrange the rest of the songs to make the playlist exciting.

Input

The first line of the input contains a single integer $t$ ($1 \le t \le 1000$) — the number of test cases. The description of test cases follows.

The first line of each test case contains a single integer $n$ ($1 \le n \le 16$) — the number of songs in the original playlist.

Then $n$ lines follow, the $i$-th of which contains two strings of lowercase letters $g_i$ and $w_i$ ($1 \leq |g_i|, |w_i| \leq 10^4$) — the genre and the writer of the $i$-th song. Where $|g_i|$ and $|w_i|$ are lengths of the strings.

The sum of $2^n$ over all test cases does not exceed $2^{16}$.

The sum of $|g_i| + |w_i|$ over all test cases does not exceed $4 \cdot 10^5$.

Output

For each test case, output a single integer — the minimum number of removals necessary so that the resulting playlist can be made exciting.

Example

input

4
1
pop taylorswift
4
electronic themotans
electronic carlasdreams
pop themotans
pop irinarimes
7
rap eminem
rap drdre
rap kanyewest
pop taylorswift
indierock arcticmonkeys
indierock arcticmonkeys
punkrock theoffspring
4
a b
c d
e f
g h

output

0
0
4
3

Note

In the first test case, the playlist is already exciting.

In the second test case, if you have the songs in the order $4, 3, 1, 2$, it is exciting, so you don't need to remove any songs.

In the third test case, you can remove songs $4, 5, 6, 7$. Then the playlist with songs in the order $1, 2, 3$ is exciting.

解題思路

  因為前段時間有事好久沒有寫部落格了,也沒有怎麼寫題。挑個上場的 Div. 4 最後一題水水()

  資料範圍的提示 "The sum of $2^n$ over all test cases does not exceed $2^{16}$." 就直接把做法貼臉上了,挺樂的()

  先定義符號 $a_i$ 和 $b_i$ 分別表示第 $i$ 首音樂的作者和流派。$g(i,j)=0/1$ 表示第 $i$ 首音樂能否與第 $j$ 首音樂相鄰,直接暴力預處理即可。定義狀態 $f(i,j)$ 表示由二進位制集合 $i$ 中的音樂(即所有是 $1$ 位)構成的所有合法序列中(且最後一首音樂為 $j$),代價的最小值。其中代價指集合中要刪除的元素數量。當然還有一個前提是 $i$ 的二進位制下的第 $j$ 位要為 $1$。

  考慮狀態轉移,如果第 $j$ 首音樂不接在任何一首音樂的後面,那麼代價就是 $\mathrm{popcount}(i)-1$,其中 $\mathrm{popcount}(i)$ 表示 $i$ 在二進位制下為 $1$ 的位數。如果要接在第 $k$ 首音樂後面,有兩個條件要滿足:$i$ 的第 $k$ 位是 $1$ 且 $g(j,k) = 1$。代價就是 $f\left( i \oplus 2^{j}, k \right)$。因此狀態轉移方程就是 $$f(i,j) = \min\left\{{\mathrm{popcount}(i)-1, \, \min\limits_{\begin{array}{c} 0 \leq k < n \\ i \bmod 2^k = 0 \\ g(j,k) = 1 \end{array}}\left\{{f\left(i \oplus 2^{j}, k\right)}\right\}}\right\}$$

  AC程式碼如下,時間複雜度為 $O\left(n \sum{|a_i| + |b_i|} + n^2 2^n \right)$:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 16, M = 1 << 16, INF = 0x3f3f3f3f;

string a[N], b[N];
int g[N][N];
int f[M][N];

void solve() {
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> a[i] >> b[i];
    }
    for (int i = 0; i < n; i++) {
        for (int j = i + 1; j < n; j++) {
            if (a[i] == a[j] || b[i] == b[j]) g[i][j] = g[j][i] = 1;
            else g[i][j] = g[j][i] = 0;
        }
    }
    for (int i = 1; i < 1 << n; i++) {
        for (int j = 0; j < n; j++) {
            if (i >> j & 1) {
                f[i][j] = __builtin_popcount(i) - 1;
                for (int k = 0; k < n; k++) {
                    if (i >> k & 1 && g[j][k]) f[i][j] = min(f[i][j], f[i ^ 1 << j][k]);
                }
            }
            else {
                f[i][j] = INF;
            }
        }
    }
    int ret = n;
    for (int i = 0; i < n; i++) {
        ret = min(ret, f[(1 << n) - 1][i]);
    }
    cout << ret << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    
    return 0;
}

參考資料

  Codeforces Round 937 (Div. 4) Editorial:https://codeforces.com/blog/entry/127664