AOJ1161 Verbal Arithmetic

問題
http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=1161

覆面算を解け。ただし、以下の条件を満たす。

  • STRING_1 + STRING_2 + ... + STRING_{N-1} = STRING_N
  • 異なる文字で同じ値が重複してはならない
  • 複数桁ある数の時、先頭の数字は0であってはならない
  • 2\lt N\lt13


解説
以下のサイトを見させて頂きました。
http://d.hatena.ne.jp/kkishi/20090707/1246930309

このサイトに書いてある以上のことは書けないので、特別解説はしません。
AOJの実行速度が昔に比べて速くなっているので、一つ目の係数のみをみる単純な解法でも十分な速度で通ります。

bool forbid_0[10];
int coef[10];
int ASize;
bool used[10];
int sum;

int dfs(int idx) {
  int ret = 0;
  if(idx == ASize) return sum == 0;

  rep(digit, 10) {
    if(digit == 0 && forbid_0[idx]) continue;
    if(used[digit]) continue;
    used[digit] = 1, sum += coef[idx] * digit;
    ret += dfs(idx + 1);
    used[digit] = 0, sum -= coef[idx] * digit;
  }
  return ret;
}

int main() {
  int N;
  while(cin >> N && N) {
    vector<string> v(N);

    string alphs;
    rep(i, N) {
      cin >> v[i];
      alphs += v[i];
    }

    // ACM, IBM, ICPC => {A,B,C,I,M,P} 文字列alphsとして順に並べる
    sort(all(alphs)); alphs.erase(unique(all(alphs)), alphs.end());

    /*
      各文字の係数を先に計算する
      ACM + IBM = ICPC より、
      100*A+10*C+M + 100*I+10*B+M = 1000*I+101*C+10*P
      100*A +10*B -91*C -900*I +2*M -10*P = 0
      これを満たすようなA, B, C, I, M, Pの値を求める
      */

    // alphs上の位置を特定する
    vector<vector<int>> pos(N);
    rep(i, N) rep(j, v[i].size()) pos[i].push_back(alphs.find(v[i][j]));

    rep(i, 10) coef[i] = 0;

    // 左辺を足す
    rep(i, N-1) rep(j, pos[i].size()) coef[pos[i][j]] += pow(10, (int)pos[i].size()-1-j);

    // 右辺を引く
    rep(i, pos.back().size()) coef[pos.back()[i]] -= pow(10, (int)pos.back().size()-1-i);

    // 0を取れないアルファベットを特定する
    rep(i, 10) forbid_0[i] = 0;
    rep(i, N) if(pos[i].size() > 1) forbid_0[pos[i][0]] = 1;

    ASize = alphs.size();

    rep(i, 10) used[i] = 0;
    sum = 0;
    cout << dfs(0) << endl;

  }

  return 0;
}