ARC099E. Independence
考虑一个子问题。给定无向图 \(G\),如何判断能否将 \(G\) 的点集分成两部分 \(S\)、\(T\) 使得 \(S\) 和 \(T\) 导出的子图都是完全图?
这个问题把我难住了。解法是考虑 \(G\) 的补图 \(G'\),\(G\) 中的完全子图对应于 \(G'\) 中的独立集。
\(G'\) 的点集能划分为两个独立集等价于 \(G'\) 是二分图。
回到原问题。对于补图 \(G'\) 的每个连通分量,二分图的两个点集是确定的。于是我们可以通过 DP 算出 \(S\) 中可能有几个点。
官方题解:
code
int main() {
int n, m;
scan(n, m);
vv a(n, vi(n));
rep (m) {
int x, y;
scan(x, y);
--x, --y;
a[x][y] = a[y][x] = 1;
}
vv g(n);
rng (i, 0, n) {
rng (j, 0, i) {
if (!a[i][j]) {
g[i].pb(j);
g[j].pb(i);
}
}
}
vi vis(n);
int c1, c2;
vpii num;
function dfs = [&](int u) {
FOR (v, g[u]) {
if (!vis[v]) {
vis[v] = -vis[u];
if (vis[v] == 1) {
++c1;
} else {
++c2;
}
dfs(v);
} else if (vis[v] != -vis[u]) {
println(-1);
exit(0);
}
}
};
rng (i, 0, n) {
if (!vis[i]) {
vis[i] = 1;
c1 = 1, c2 = 0;
dfs(i);
num.eb(c1, c2);
}
}
vi dp(n + 1);
dp[0] = 1;
int limit = 0;
FOR (p, num) {
down (i, limit, 0) {
if (dp[i]) {
dp[i] = 0; // 这里容易错。少了这一句就错了。
dp[i + p.first] = 1;
dp[i + p.second] = 1;
}
}
limit += max(p.first, p.second);
}
int ans = INT_MAX;
rng (i, 0, n + 1) {
if (dp[i]) {
chkmin(ans, i * (i - 1) / 2 + (n - i) * (n - i - 1) / 2);
}
}
println(ans);
return 0;
}
以上实现在 DP 部分采用了滚动数组的技巧,要注意及时清空上一轮的状态。