题目链接:2019 牛客多校第五场 E
最多 26 个点,\(2^n \approx 6 \times 10^7\),考虑用二进制压缩状态。状态 \(s\) 的第 \(i\) 位表示取或者不取第 \(i\) 个点。
我们用 \(f(s)\) 表示状态 \(s\) 的最大独立集的个数,可以从下面两个状态推得:二进制 \(s\) 的最后一个为1的位置不属于最大独立集 \(f(s) = f(s - {\rm lowbit}(s) )\) 二进制 \(s\) 的最后一个为1的位置属于最大独立集 \(f(s) = f(s - {\rm adj}(u)) + 1\) 其中 \({\rm adj}(u)\) 表示最后一个为 1 的位置和它相邻节点的集合,即 \(v\) 在最大独立集中,则与它邻接的点一定不在最大独立集中。二者取最大即可。
时间复杂度 \(O(2^n)\)。
需要特别注意这题空间卡得很紧,需要用 char
来存答案。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| #include <bits/stdc++.h> using namespace std; typedef long long LL; const int MAX_N = 26; const int MAX_S = (1 << 26); int n, m, all; vector <int> g[MAX_N]; int c[MAX_N]; char f[MAX_S]; char dfs(int s) { if (f[s] != -1) return f[s]; int u = s, pos = 0; while (!(u & 1)) ++pos, u >>= 1; short notInSet = dfs(s - (1 << pos)); short inSet = 1 + dfs(s & c[pos]); f[s] = max(inSet, notInSet); return f[s]; } int main() { scanf("%d%d", &n, &m); all = (1 << n) - 1; int u, v; for (int i = 1; i <= m; ++i) { scanf("%d%d", &u, &v); g[u].push_back(v); g[v].push_back(u); } for (int i = 0; i < n; ++i) { c[i] += (1 << i); for (auto const &e: g[i]) { c[i] += (1 << e); } c[i] = c[i] ^ all; } fill(f, f + MAX_S, -1); f[0] = 0; LL ans = 0; for (int i = 0; i <= all; ++i) ans += int(dfs(i)); printf("%lld\n", ans); return 0; }
|