【精進】ARC099 E - Independence
【精進】ARC099 E - Independence
補グラフ、2部グラフ、DP
問題概要
E - Independence
$N$頂点$M$辺の無向グラフがある。グラフを2つの完全グラフに分けることができるかを判定し、できるなら両グラフの辺の数の和の最小値を求めよ。
解法
補グラフを考える。頂点を$S,T=[N]\setminus S$に分けるとき、辺は$S,T$間にのみ張られている必要がある。従って補グラフが2部グラフでないなら不可能である。連結成分ごとに考える。連結な2部グラフは一意に2つの互いに隣接しない頂点集合に分けることができ、一方が$S$,他方が$T$に含まれる。各連結成分のそのような頂点集合のサイズを$x_i,y_i$とすると求めるのは
\[\begin{align*} \text{minimize}\quad &\frac{|S| (|S|-1)}{2}+\frac{|T| (|T|-1)}{2} \\ \text{subject to}\quad & |S| = \sum_i z_i \\ & |T| = N - |S| \\ & z_i \in \{x_i,y_i\} \end{align*}\]になる。 そのためには$|S|$としてあり得るものを列挙できればよいが、連結成分の個数も$|S|$も$N$以下なのでDPを用いて計算できる。
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include "template.hpp"
pair<Graph, ll> make_g() {
LL(n, m);
vector _g(n, vector<bool>(n, true));
rep(i, m) {
LL(a, b);
_g[--a][--b] = false;
_g[b][a] = false;
}
Graph g(n);
rep(i, n) rep(j, n) if(i != j and _g[i][j]) g[i].push_back(j);
return {g, n * (n - 1) / 2 - m};
}
void solve() {
auto [g, m] = make_g();
ll n = g.size();
vl used(n, -1);
vl zo(2);
queue<ll> q;
vector<vector<ll>> add_sz;
rep(i, n) {
zo[0] = zo[1] = 0;
if(used[i] != -1)
continue;
zo[0]++;
used[i] = 0;
q.push(i);
while(q.size()) {
ll now = q.front();
q.pop();
for(auto nex : g[now]) {
if(used[nex] == used[now]) {
// 2部グラフでない
print(-1);
return;
}
if(used[nex] == -1) {
used[nex] = 1 ^ used[now];
zo[used[nex]]++;
q.push(nex);
}
}
}
add_sz.push_back(zo);
}
vector<bool> dp(n + 1, false);
dp[0] = true;
for(auto v : add_sz) {
auto prev = dp;
fill(all(dp), false);
rep(i, n + 1) {
if(prev[i])
rep(j, 2) if(i + v[j] <= n) {
dp[i + v[j]] = true;
}
}
}
ll ans = inf;
rep(i, n + 1) {
if(dp[i]) {
chmin(ans, i * (i - 1) / 2 + (n - i) * (n - i - 1) / 2);
}
}
print(ans);
}
int main() {
ios::sync_with_stdio(false);
std::cin.tie(nullptr);
solve();
}
This post is licensed under CC BY 4.0 by the author.