SRM 658 div1 easy: OddEvenTree
問題概要
N頂点(頂点0~N-1)の木の距離行列xが与えられる。
0 <= i, j <= N-1を満たす全ての(i, j)組について
x[i][j] = 'O'ならば頂点(i, j)間が奇数長
x[i][j] = 'E'ならば頂点(i, j)間が偶数長
が成り立つような、木は存在するだろうか。
存在しなければ{-1}を返し、存在すれば、そのような木を一つ出力せよ。
解法
木が存在するためには、以下の条件を全て満たす必要がある。
・xが対称行列
・全てのiについて、x[i][i] == 'E'
・任意の3頂点A, B, Cについて、AB間の距離 + BC間の距離 + CA間の距離が偶数
・一つ以上'O'が存在
なお、これは十分条件ではない(テストケース88は、これらすべてを満たすが、{-1}を返すべきケース)
上の条件をすべて満たしたとき、木の構築をする。
木の構築は、以下の貪欲アルゴリズムで行い、全部つなげなければ{-1}を返すようにしたら通った。
・頂点0を接続済み頂点にする。他の頂点は「接続済み頂点」ではない。
・全ての頂点を「接続済み頂点」にするか、つなげなくなるまで以下を繰り返す。
・「接続済み頂点A」と「接続済みでない頂点B」を見つけたら、頂点AとBを結ぶ。
・見つからなければ、{-1}を返す。
・このアルゴリズムを使って木を構築したとき、条件を満たさない木が出来たり、条件を満たす木が存在するのに{-1}を返したり しなかった。理由は良くわからない。
//必要条件だけで枝刈り&条件を満たしたらすぐにつなぐ、という嘘っぽいあれ。 //木の構築中に矛盾が生じたらfalseでも良かったけど、見落としがあると不味いので。 #include <iostream> #include <vector> #include <string> using namespace std; class OddEvenTree{ public: vector<int> False(){ vector<int> ret; ret.push_back(-1); return ret; } vector<int> getTree(vector<string> x) { int i, j, k; int n = x.size(); int Ecnt = 0; for( i = 0; i < n; i++ ){ for( j = 0; j < n; j++ ){ if( x[i][j] != x[j][i] ) return False(); if( i == j && x[i][i] == 'O' ) return False(); Ecnt += (x[i][j] == 'E'); } } if( Ecnt == 0 ){ return False(); } for( i = 0; i < n; i++ ){ for( j = 0; j < n; j++ ){ for( k = 0; k < n; k++ ){ int d = (x[i][j] == 'O') + (x[j][k] == 'O') + (x[k][i] == 'O'); if( d % 2 == 1 ) return False(); } } } //木の構築(エラーは検出済みなので、これで構築できなかったら最適な構築ではない) //ばばーん //(貪欲に構築しているため、条件を満たさない木が出来るかもしれない…) bool used[50] = {false}; int sz = 1; vector<int> ans; used[0] = true; while( sz < n ){ bool okflag = false; for( i = 0; i < n; i++ ){ for( j = 0; j < n; j++ ){ if( used[i] && !used[j] && x[i][j] == 'O' ){ used[j] = true; ans.push_back(i); ans.push_back(j); okflag = true; sz++; } } } if( !okflag ){ return False(); } } return ans; } };
提出コードのコメントがひどい