NEERC 2010 K - Graph Oddity
問題概要
n 頂点 m 辺の単純で連結な無向グラフが与えられる。n は奇数である。
deg(v) を頂点 v の次数とし、k を deg(v) の最大値以上で最小の奇数と定義する。
このとき、グラフを k 色以下で頂点彩色せよ。
・制約
3 <= n <= 9999
1 <= m <= 100000
n は奇数
解法
n と k が両方奇数なので、k 色以下で塗る方法は必ず存在することが示せる。
次数の大きい方からとっていくだけだとうまくいかない。
そこで、以下のアルゴリズムを考える。
- S をすべての頂点からなる集合とする
- S が空でないなら、S で deg(v) が最小のものを取り出し、v とする
- 配列 ord の末尾に v を追加
- v の隣接頂点の次数をデクリメントする
- 2 に戻る
このとき、ord[i] について以下の不変条件が成り立つ。
「頂点 ord[i] は、j < i なる任意の頂点 ord[j] に色が塗られていなければ、ほかはどのように彩色されていても、ある色で塗ることができる」
以下、大雑把な証明。
- deg(ord[i]) < k ならば、そもそも好きに色をぬることができる。
- deg(ord[i]) = k となることはない。もし成り立つとすると ord[i] の隣接頂点は一度も v として選ばれておらず、deg(u) = k (u は隣接頂点) が成り立つ。同様に u の隣接頂点も選ばれていないから…と議論が続き、結局この段階では ord[i] 以外の任意の頂点が選ばれていないことになる。すなわち、G は完全グラフである。k が奇数なので、G は偶数個の頂点を含むが、これは矛盾である。
よって、あとは ord[i] の逆順に色を適当に塗っていけばうまくいく。
要は自由に塗れるやつは後回しにする、という発想でしかない。
ソースコード
#include <bits/stdc++.h> using namespace std; using pii = pair<int, int>; int main() { ifstream ifs("kgraph.in"); int n, m; ifs >> n >> m; vector<vector<int>> g(n); vector<int> deg(n); int k = 0; for(int i = 0; i < m; ++i) { int a, b; ifs >> a >> b; a--, b--; g[a].push_back(b); g[b].push_back(a); deg[a] += 1; deg[b] += 1; k = max({k, deg[a], deg[b]}); } k += k % 2 == 0; set<pii> s; // (deg, v) vector<int> ord(n); for(int v = 0; v < n; ++v) s.emplace(deg[v], v); for(int i = 0; i < n; ++i) { const int v = s.begin()->second; s.erase(s.begin()); ord[i] = v; for(auto to : g[v]) { if(s.count(make_pair(deg[to], to))) { s.erase(make_pair(deg[to], to)); deg[to]--; s.emplace(deg[to], to); } } } vector<int> ans(n, -1); for(int i = n - 1; i >= 0; --i) { const int v = ord[i]; vector<bool> used(k + 1); for(auto to : g[v]) { if(ans[to] == -1) continue; used[ans[to]] = true; } for(int j = 1; j <= k; ++j) { if(!used[j]) { ans[v] = j; break; } } } ofstream ofs("kgraph.out"); ofs << k << '\n'; for(int i = 0; i < n; ++i) { ofs << ans[i] << '\n'; } }