SRM527 Div1 Medium - P8XMatrixRecovery
問題
h行w列で各要素が '0' か '1' か '?' のみからなる行列が2つ与えられる(それぞれhs, wsとする)
これらの行列の '?' を0か1に書き換えて同じ行列にするとき、辞書順最小のものを求めよ
1 <= h, w <= 30
2つの行列を一致させる方法が少なくとも1つは存在することが保証される
解法
辞書順最小の条件より、0にしても問題が起こらない '?' は全部0にするべき
手前の要素から順に見ていき、'?' を見つける度にとりあえず0にしてみて、それで問題が起こらなければそのまま次に進めば良いし、問題が起きるなら1にするしかない
これを繰り返せば良さそう
あとは問題が起きるかどうかを判定する方法さえわかれば解ける
hsの列を一個ずつ見ていき、wsの列のうち両者の '?' をうまく変えれば一致させることができるような列と辺で結ぶ
すると二部グラフができる
条件を満たすような '?' の埋め方が存在するということは、この二部グラフに完全マッチングが存在することと同値である
二部グラフの最大マッチングはFordFulkerson法とかDinic法で求められる
→ 解けた🤠
計算量はO(hw^3(h+w))くらいかな
実装
dinicは蟻本p194の丸パクリ
二部グラフの頂点番号は、始点を0、hsの各列を1〜w、wsの各列をw+1〜2w、終点を100とした
始点からhsの各列へ容量1の辺を貼り、hsの各列からwsの(一致させられる)各列へ容量1の辺を貼り、wsの各列から終点へ容量1の辺を貼る
最初TLEしてどこで無駄なことしてんのかなって結構悩んだんだけど、デバッグ用の出力でめっちゃ時間かかってるだけだった
今後気を付ける
const int MAXV = 123; class Dinic { private: struct edge {int to, cap, rev;}; vector<edge> g[MAXV]; int level[MAXV], iter[MAXV]; public: // fromからtoへ向かう要領capの辺をグラフに追加する void add_edge(int from, int to, int cap) { g[from].push_back((edge){to, cap, (int)g[to].size()}); g[to].push_back((edge){from, 0, (int)g[from].size()-1}); } // sからの最短距離をBFSで計算 void bfs(int s) { memset(level, -1, sizeof(level)); queue<int> que; level[s] = 0; que.push(s); while (!que.empty()) { int v = que.front(); que.pop(); for (int i = 0; i < g[v].size(); i++) { edge &e = g[v][i]; if (e.cap>0 && level[e.to]<0) { level[e.to] = level[v] + 1; que.push(e.to); } } } } // 増加パスをdfsで探す int dfs(int v, int t, int f) { if (v==t) return f; for (int &i = iter[v]; i < g[v].size(); ++i) { edge &e = g[v][i]; if (e.cap>0 && level[v]<level[e.to]) { int d = dfs(e.to, t, min(f, e.cap)); if (d>0) { e.cap -= d; g[e.to][e.rev].cap += d; return d; } } } return 0; } // sからtへの最大流を求める int getflow(int s, int t) { int flow = 0; while (1) { bfs(s); if (level[t]<0) return flow; memset(iter, 0, sizeof(iter)); int f; while ((f=dfs(s, t, inf))>0) flow += f; } } }; class P8XMatrixRecovery { public: int h, w; vector<string> rs, cs; bool ok() { Dinic din; rep(i,w) rep(j,w) { bool ng = 0; rep(k,h) if (rs[k][i]!='?' && cs[j][k]!='?' && rs[k][i]!=cs[j][k]) { ng = 1; break; } if (!ng) din.add_edge(i+1,w+j+1,1); } rep(i,w) din.add_edge(0,i+1,1), din.add_edge(w+i+1,100,1); return din.getflow(0,100)==w; } vector<string> solve(vector<string> R, vector<string> C) { rs = R, cs = C, h = rs.size(), w = cs.size(); rep(i,h) rep(j,w) if (rs[i][j]=='?') { rs[i][j] = '0'; if (!ok()) rs[i][j] = '1'; } return rs; } };