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 部分采用了滚动数组的技巧,要注意及时清空上一轮的状态。

posted @ 2019-11-21 14:27  Pat  阅读(204)  评论(0编辑  收藏  举报