AOJ : 1122 - What is the Number in my Mind ?

問題概要

http://rose.u-aizu.ac.jp/onlinejudge/ProblemSet/description.jsp?id=1122
謎の nケタの数字がある.
問題では, nケタの予想数とそれに関するヒット数&ブロー数が, m回入力される.
このとき, 謎の数字を当てる問題. ただし, 当てられない場合とか1通りに決まらないときは, NOと出力.

アルゴリズム

単純なO(10!)の全探索ではとおらないようです.(国内予選本番では大丈夫でしょうが)
というわけで, 少し枝刈して無理やり通しましょう.


ちなみに, 僕の場合は, ヒット数が 0 の場合の入力を利用して枝刈しました.
(運まかせなやり方)

プログラム

int n,m;
int h[100],b[100];
bool bad[10][10]; //各桁で使ってはいけない値
vector<int> t[100],val,ans;

//ヒット数とブロー数が合っているかの判定
bool check(bool *used){
  for(int i=0;i<m;i++){
    int hit=0, blow=0;
    for(int j=0;j<n;j++){
      if(val[j] == t[i][j]){
        hit++;
      }
      else if(used[t[i][j]]){
        blow++;
      }
    }
    if(hit!=h[i] || blow!=b[i]) return false;
  }
  return true;
}

bool solve(int idx,bool *used){
  if(idx == n){
    if(!check(used)) return true;
    if(!ans.empty()) return false; //2つ以上答えがあるとNO
    ans = val;
    return true;
  }

  for(int i=0;i<10;i++){
    if(used[i] || bad[idx][i]) continue;
    used[i] = true;
    val[idx] = i;
    if(!solve(idx+1,used)) return false;
    used[i] = false;
  }

  return true;
}

int main(void){
  while(cin>>n>>m,n||m){
    memset(bad,0,sizeof(bad));
    for(int i=0;i<m;i++){
      string s;
      cin>>s>>h[i]>>b[i];
      t[i].clear();
      for(int j=0;j<n;j++){
        t[i].push_back(s[j]-'0');

        //ヒット数が0なら各桁で使ってはいけない値がわかる
        if(h[i] == 0) bad[j][t[i][j]] = true;
      }
    }

    bool used[10];
    memset(used,0,sizeof(used));
    ans.clear();
    val = vector<int>(n);
    bool flg = solve(0,used);

    if(flg && !ans.empty()){
      for(int i=0;i<n;i++) cout<<ans[i];
      cout<<endl;
    }
    else{
      cout<<"NO\n";
    }
  }

  return 0;
}