SRM 386 250pt: CandidateKeys
問題概要
DBの表がある。各列の組み合わせを抽出したものが各行に対してユニークに定まるのであれば、その組み合わせを主キーと呼ぶことにする。極小な主キーの組み合わせとして、最大の数と最小の数を求める問題。行数Rは50以下、列数Cは10以下。
考えたこと
- 問題がよく分からない。
- しばらく経ってようやく問題を理解した。で、どうするか。
- 主キーの組み合わせを列挙するのはO(2^C * C * R)でいける。
- 極小の判定どうしよう。グラフ構成するのとか面倒だからやりたくない。
- いやいや、グラフ構成する位なら候補数Nに対してO(N^2)でいけるじゃないか。
- 後は書くだけ。サンプル通った。投げた。WA。
- ビット回す範囲が2^Rまでになってた。2^Cに修正して無事AC。本番でこの間違いしたら悔しいだろうなあ。
practiceで通ったコード
#include <vector> #include <string> #include <set> #include <algorithm> using namespace std; const int INF = 1<<29; struct CandidateKeys { vector <int> getKeys(vector <string> table) { const int N = table.size(), M = table[0].length(); vector<int> cands; //全通り試す for(int bit=0; bit<(1<<M); bit++){ set<string> keys; for(int i=0; i<N; i++){ string str; for(int j=0; j<M; j++)if((bit>>j)&1){ str += table[i][j]; } keys.insert(str); } if((int)keys.size() == N){ cands.push_back(bit); } } if(cands.empty()){ return vector<int>(); } //極小判定 int mi = INF, ma = 0; for(int i=0; i<(int)cands.size(); i++){ bool ok = true; for(int j=0; j<(int)cands.size(); j++)if(i!=j){ ok &= !( cands[i] == (cands[i] | cands[j]) ); } if(ok){ ma = max(ma, __builtin_popcount(cands[i])); mi = min(mi, __builtin_popcount(cands[i])); } } vector<int> ret(2); ret[0] = mi; ret[1] = ma; return ret; } };