算法
(状压DP) $O(3^N)$
题意是对于同一连通分量里的所有点必须要能构成完全图,要求最小化这种连通分量的个数。
注意到 $N$ 最大只有 $18$,我们可以很自然地联想到状压DP
,但在此之前需要先判断一下原图的每个子图是否为完全图:若只有一个点,则它显然是完全图;否则我们可以取出当前子图的第一个点,然后验证它和剩下的点是否能直接相连,如果有一点没有直接相连,那么当前子图就不是完全图,而如果都都能直接相连,那么当前子图是否是完全图取决于剩下的点构成的图是否是完全图。
C++ 代码
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
void chmin(int& a, int b) { if (a > b) a = b; }
int main() {
cin.tie(nullptr) -> sync_with_stdio(false);
int n, m;
cin >> n >> m;
vector g(n, vector<int>(n));
rep(i, m) {
int a, b;
cin >> a >> b;
--a; --b;
g[a][b] = g[b][a] = 1;
}
int n2 = 1 << n;
vector<int> d(n2);
rep(s, n2) {
if (__builtin_popcount(s) == 1) {
d[s] = 1;
continue;
}
int b = s&-s;
int ns = s^b;
d[s] = d[ns];
int j = __builtin_popcount(b-1);
rep(i, n) if (ns>>i&1) {
if (!g[i][j]) d[s] = 0;
}
}
const int INF = 1001001001;
vector<int> dp(n2, INF);
d[0] = 0;
rep(s, n2) {
if (d[s]) dp[s] = 1;
int b = s&-s;
int ns = s^b;
for (int t = ns; t; t = (t-1)&ns) if (d[t]) {
chmin(dp[s], dp[s^t]+1);
}
}
cout << dp.back() << '\n';
return 0;
}