朝日新聞2005年10月21日パズル横丁解答

(2005.11.04)新聞の解答欄によれば以下の解答は間違い。正解は最初が「ある」で,以降全部「ない」ということだ。正解者は3名ということだが,星2つ問題でなく,星3つ問題として出題されていたら正解率はもっと上がっただろうに。
(2005.11.07)嘘つき問題はやはり苦手だ。やっぱり判りづらい。以下解答を抜粋する。
-----ここから-----

最初の□に「ある」そのあとの□全部に「ない」を入れる。
□が二つの場合を考えてみます。
「パズル君は「自分は「自分はうそつきで□」と言ったことが□」と言った」
という文で,パズル君が正直者の時につじつまがあい,うそつきの時に矛盾する内容になるのは,□が順に「ある」「ない」の時だけです。
うそつきの場合,「自分は「○○」と言ったことがない」というのは「○○」と言ったことになり,「」が次々にはずれ,答えにたどりつきます。
応募111通で正解率3%。
-----ここまで-----

プログラムの実行結果は以下の通り。

嘘つきは「私は嘘つきでない(偽)」と言ったことが⇒ ない⇒ ない⇒ ない⇒ ない⇒ ない⇒ ない ⇒この発言は 偽 である★
正直者は「私は嘘つきでない(真)」と言ったことが⇒ ある⇒ ある⇒ ある⇒ ある⇒ ある⇒ ある ⇒この発言は 真 である★

正直者の行の発言が正解かな。問題文に合わせると,

「自分は
「自分は
「自分は
「自分は
「自分は
「自分は
「自分は嘘つきでない」
と言ったことがある」
と言ったことがある」
と言ったことがある」
と言ったことがある」
と言ったことがある」
と言ったことがある」

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

#include "puzutl.h"

const int N6 = 6;               // 6回「ある」,「ない」を繰り返す
const int N = 1<<N6;            // 6回の「ある」,「ない」の組合せ

YesNo logarunai[N6+10];
YesNo logtf[N6+10];

void check( YesNo man/*正直者(YES)/嘘つき(NO)*/,
            YesNo usotuki/*最初の「私は嘘つきで(ある/ない)*/,
            YesNo truefalse/*私は○○と言ったことが{ある|ない},○○の真偽*/,
            int cnt/*回数*/,
            int diffarunai/*正直者は○○が(真)なら「ある」と言い,偽なら「ない」と言う*/
                          /*嘘つきは○○が(真)なら「ない」と言い,偽なら「ある」と言う*/
            )
{
  if( man != truefalse) return;
  if( cnt >= N6) {
    ps( "%sは「私は嘘つきで%s(%s)」と言ったことが",man?"正直者":"嘘つき", usotuki?"ある":"ない", logtf[0]?"真":"偽");
    for( int i=0; i< N6; i++) {
      ps( "⇒ ");
      ps( "%s", logarunai[i]?"ある":"ない");
    }
    ps( " ⇒この発言は %s である", truefalse?"真":"偽");
    if( man == truefalse) ps( "★");
    ps( "\n");
    return;
  }
  logarunai[cnt] = diffarunai&1;
  logtf[cnt]     = truefalse;

  if(  man &&  truefalse && ((diffarunai&1)==1)) check( man, usotuki, YES, cnt+1, diffarunai>>1); // (正直者)は「(真)を言ったことが(ある)」⇒真
  if(  man &&  truefalse && ((diffarunai&1)==0)) check( man, usotuki, NO , cnt+1, diffarunai>>1); // (正直者)は「(真)を言ったことが(ない)」⇒偽
  if(  man && !truefalse && ((diffarunai&1)==1)) check( man, usotuki, NO , cnt+1, diffarunai>>1); // (正直者)は「(偽)を言ったことが(ある)」⇒偽
  if(  man && !truefalse && ((diffarunai&1)==0)) check( man, usotuki, YES, cnt+1, diffarunai>>1); // (正直者)は「(偽)を言ったことが(ない)」⇒真

  if( !man &&  truefalse && ((diffarunai&1)==1)) check( man, usotuki, NO,  cnt+1, diffarunai>>1); // (嘘つき)は「(真)を言ったことが(ある)」⇒偽
  if( !man &&  truefalse && ((diffarunai&1)==0)) check( man, usotuki, YES, cnt+1, diffarunai>>1); // (嘘つき)は「(真)を言ったことが(ない)」⇒真
  if( !man && !truefalse && ((diffarunai&1)==1)) check( man, usotuki, YES, cnt+1, diffarunai>>1); // (嘘つき)は「(偽)を言ったことが(ある)」⇒真
  if( !man && !truefalse && ((diffarunai&1)==0)) check( man, usotuki, NO,  cnt+1, diffarunai>>1); // (嘘つき)は「(偽)を言ったことが(ない)」⇒偽
}
int main( int argc, cstring argv[])
{
  for( int i=0; i< N; i++) {  // ある,ないの組合せ,下位ビットからある(1),ない(0)を取り出す
    check( YES/*正直者が*/, YES/*「私は嘘つきで(ある)」*/, NO /*は(偽)*/, 0/*6回*/, i/*と言ったことが「ある(ON)/なし(OFF)」の組合せ*/);
    check( NO /*嘘つきが*/, YES/*「私は嘘つきで(ある)」*/, YES/*は(真)*/, 0/*6回*/, i/*と言ったことが「ある(ON)/なし(OFF)」の組合せ*/);
    check( YES/*正直者が*/, NO /*「私は嘘つきで(ない)」*/, YES/*は(真)*/, 0/*6回*/, i/*と言ったことが「ある(ON)/なし(OFF)」の組合せ*/);
    check( NO /*嘘つきが*/, NO /*「私は嘘つきで(ない)」*/, NO /*は(偽)*/, 0/*6回*/, i/*と言ったことが「ある(ON)/なし(OFF)」の組合せ*/);
  }
  return    0;
}