朝日新聞2006年1月20日パズル横丁解答

プログラムの実行結果は以下の通り。☆のあるところが解。次郎は「いぬ」と答えるべき。今年は「戌年(いぬどし)」だからか。

太郎 次郎 => 勝ち 負け
あい あい =>    0    0
あい いか =>    0    1
あい いし =>    0    1
あい いぬ =>    1    0 ☆
あい いま =>    0    1
あい えと =>    0    0
あい かき =>    0    0
あい きく =>    0    0
あい くち =>    0    0
あい しか =>    0    0
あい ちえ =>    0    0
あい とし =>    0    0
あい ぬか =>    0    0
あい まめ =>    0    0
あい めし =>    0    0

プログラムのソースは以下の通り。

#include "puzutl.h"

cstring word[] = { "あい",      // 太郎と次郎が知っている全ての単語
                   "いか",
                   "いし",
                   "いぬ",
                   "いま",
                   "えと",
                   "かき",
                   "きく",
                   "くち",
                   "しか",
                   "ちえ",
                   "とし",
                   "ぬか",
                   "まめ",
                   "めし"};
const int num_word = sizeof(word) / sizeof(word[0]); // 単語数
int nextword[num_word][num_word]; // しりとり出来る単語の並び
int num_nextword[num_word];     // しりとり出来る単語の数
int num_win[num_word];          // 次郎の2番目の単語での勝ち数
int num_loss[num_word];         // 次郎の2番目の単語での負け数
YesNo word_used[num_word];      // 単語は既に使われたか?
int find_word[num_word];        // 太郎・次郎が言った単語
void find_next( int lvl, int ab, int preword) // 次の単語を言う
{
  // lvl        :次の単語
  // ab         :==0:次は太郎
  //             !=0:次は次郎
  // preword    :前の人が言った単語
  if( lvl > num_word) {         // 最後までしりとりできた,次の言葉はもう無いので次の人が負け
    if( ab) {                   // 次郎の負け
      num_loss[find_word[1]]++; // 次郎が二番目に言った単語の勝ち負けを記録する
    }
    else {                      // 次郎の勝ち
      num_win[find_word[1]]++;  // 次郎が二番目に言った単語の勝ち数を増やす
    }
    return;
  }
  // 次の単語を考える。
  int           num_nw = 0;
  for( int i=0; i< num_nextword[preword]; i++) {// 自分が次に言える単語を調べる
    int nw = nextword[preword][i]; // 次に言える単語の候補
    if( word_used[nw]) continue; // 残念この単語は既に使っていた
    num_nw++;
    word_used[nw]   = YES;      // この単語は私が言いました
    find_word[lvl] = nw;        // この単語は太郎・次郎が既に言った
    find_next( lvl+1/*次の単語を言ってください*/, !ab, nw);
    word_used[nw]   = NO; // この単語はまた使っても良いです
  }
  if( num_nw == 0) {            // 次の単語を言えなかったので負け
    if( ab) {                   // 次郎の負け
      num_loss[find_word[1]]++; // 次郎が二番目に言った単語の勝ち負けを記録する
    }
    else {                      // 次郎の勝ち
      num_win[find_word[1]]++;  // 次郎が二番目に言った単語の勝ち数を増やす
    }
  }
}

int main( int argc, cstring argv[])
{
  // しりとりできる単語の繋がりを求める
  for( int i=0; i< num_word; i++) {
    jap         chr_last = lastjap(word[i]); // 前の単語の最後の文字
    int         n = 0;
    for( int j=0; j< num_word; j++) { // 次の単語全てを調べる
      if( chr_last == firstjap( word[j])) { // 前の単語と後ろの単語が繋がる
        nextword[i][n++] = j;   // 次の単語を記録しておく
      }
    }
    num_nextword[i] = n;        // word[i]に繋がる単語の数
    word_used[i] = NO;          // 単語はまだ使われていない
  }
  int lvl = 0;
  find_word[lvl] = 0;           // 「あい」から始める
  find_next( lvl+1, 1/*次は次郎の番*/, 0/*最初は「あい」と言った*/);
  // 単語と勝ち負けの数を出力してみる
  ps( "太郎 次郎 => 勝ち 負け\n");
  for( i=0; i< num_word; i++) {
    ps( "%s %s => %4d %4d", word[0], word[i], num_win[i], num_loss[i]);
    if( num_win[i] > 0 && num_loss[i] == 0) ps( " ☆\n");
    else ps( "\n");
  }
  return    0;
}