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

(2005.11.11)新聞の答えを見ると四則演算を一つずつ使わなくても良いみたいだ。
私は一つずつ使うと読んだんだけど新聞の答えはそうなっていない。
1つずつ使うので答えが出てしまったからそれで合っていると思った。
新聞では以下の2通りが解答。
11-(111111-1111)÷(11111-111)
111111-(11111×(111-1111÷11))
2週続けて間違いを犯したかと思って検算してみると私の答えも1になる。あってるよね。
新聞の解答と違うけど計算結果が1になるので,一応正解と見なして白星(☆)とする。

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

1111  11  ÷ 111  − 11111  × 111111  + 
11111  1111  11  ÷ 111  − × 111111  + 
111111  1111  11  ÷ 111  − 11111  × + 
111111  11111  1111  11  ÷ 111  − × + 

ちょっと見づらいが以下のような計算式である。

( 1111 ÷ 11 − 111 ) × 11111 + 111111
11111 × ( 1111 ÷ 11 − 111 ) + 111111
111111 + ( 1111 ÷ 11 − 111 ) × 11111
111111 + 11111 × ( 1111 ÷ 11 − 111 )

(2005.11.11)四則演算を1つずつに限定しないようにプログラムを修正した結果は以下の通り。10秒くらい掛かるようになってしまった。

11  1111  111111  − 111  11111  − ÷ − 
11  1111  111111  − 11111  111  − ÷ + 
11  111111  1111  − 111  11111  − ÷ + 
11  111111  1111  − 11111  111  − ÷ − 
1111  11  ÷ 111  − 11111  × 111111  + 
1111  111111  − 11111  111  − ÷ 11  + 
11111  1111  11  ÷ 111  − × 111111  + 
111111  111  1111  11  ÷ − 11111  × − 
111111  1111  11  ÷ 111  − 11111  × + 
111111  1111  − 111  11111  − ÷ 11  + 
111111  11111  111  1111  11  ÷ − × − 
111111  11111  1111  11  ÷ 111  − × + 

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

#include "puzutl.h"

YesNo used[9];                  // 
int numop[9] = {/*0〜4に5個の数字を割り当てる*/ 11,111,1111,11111,111111, /*以降演算子*/ 1/*+*/,2/*−*/,3/*×*/,4/*÷*/};
int cal[9];
cstring opstr[] = { "NA", "+", "−", "×", "÷"};

void calcRPN( int cal[])
{
  // cal[] を逆ポーランド記法で数字と演算子が入っていると仮定し計算する。
  // エラーがあれば計算を打ち切り,エラーが無ければ結果を計算する。
  int bunsi[9], bunbo[9], sp = 0;
  for( int i=0; i< 9; i++) {
    if( cal[i] > 10) { // 数字なのでスタックに積む
      bunsi[sp] = cal[i];
      bunbo[sp] = 1;
      sp++;
    }
    else if( sp > 1) {          // 演算子なので計算する。但し数字が2つあることが前提。
      int op = cal[i];
      int m = bunsi[sp-2], n = bunbo[sp-2]; // m/n op t/s
      int t = bunsi[sp-1], s = bunbo[sp-1];
      if( op == 1/*+*/) { --sp; bunsi[sp-1] = m*s + n*t; bunbo[sp-1] = s*n;}
      if( op == 2/*−*/) { --sp; bunsi[sp-1] = m*s - n*t; bunbo[sp-1] = s*n;}
      if( op == 3/*×*/) { --sp; bunsi[sp-1] = m*t      ; bunbo[sp-1] = s*n;}
      if( op == 4/*÷*/) { --sp; bunsi[sp-1] = m*s      ; bunbo[sp-1] = n*t;}
    }
    else {
      // 計算不能
      return;
    }
  }
  if( bunsi[0] == bunbo[0]) {   // ( 分子 == 分母 ) ⇒ 1
    // 計算式を出力する。式は逆ポーランドのままなのでちょっと読みにくい。
    for( int i=0; i< 9; i++) {
      if( cal[i] > 10) ps( "%d  ", cal[i]);
      else             ps( "%s ", opstr[cal[i]]);
    }
    ps( "\n");
  }
}

int main( int argc, cstring argv[])
{
  for( int a=0; a< 9; a++) {
    used[a] = YES;
    for( int b=0; b< 9; b++) {
      if( used[b]) continue;
      used[b] = YES;
      for( int c=0; c< 9; c++) {
        if( used[c]) continue;
        used[c] = YES;
        for( int d=0; d< 9; d++) {
          if( used[d]) continue;
          used[d] = YES;
          for( int e=0; e< 9; e++) {
            if( used[e]) continue;
            used[e] = YES;
            for( int f=0; f< 9; f++) {
              if( used[f]) continue;
              used[f] = YES;
              for( int g=0; g< 9; g++) {
                if( used[g]) continue;
                used[g] = YES;
                for( int h=0; h< 9; h++) {
                  if( used[h]) continue;
                  used[h] = YES;
                  for( int i=0; i< 9; i++) {
                    if( used[i]) continue;
                    used[i] = YES;
                    // 演算式をスタックに積んで計算する。
                    cal[0] = numop[a];
                    cal[1] = numop[b];
                    cal[2] = numop[c];
                    cal[3] = numop[d];
                    cal[4] = numop[e];
                    cal[5] = numop[f];
                    cal[6] = numop[g];
                    cal[7] = numop[h];
                    cal[8] = numop[i];
                    // 実際に計算し結果が1になるものが解
                    calcRPN( cal);
                    used[i] = NO;
                  }
                  used[h] = NO;
                }
                used[g] = NO;
              }
              used[f] = NO;
            }
            used[e] = NO;
          }
          used[d] = NO;
        }
        used[c] = NO;
      }
      used[b] = NO;
    }
    used[a] = NO;
  }
  return    0;
}

(2005.11.11)四則演算を1つずつに限定しないようにプログラムを修正した結果のソースは以下の通り。
修正個所を赤字にした。

#include "puzutl.h"

int used[9];                  // 
int numop[9] = {/*0〜4に5個の数字を割り当てる*/ 11,111,1111,11111,111111, /*以降演算子*/ 1/*+*/,2/*−*/,3/*×*/,4/*÷*/};
int nummax[9] = { 1, 1, 1, 1, 1, 4, 4, 4, 4};
int cal[9];
cstring opstr[] = { "NA", "+", "−", "×", "÷"};

void calcRPN( int cal[])
{
  // cal[] を逆ポーランド記法で数字と演算子が入っていると仮定し計算する。
  // エラーがあれば計算を打ち切り,エラーが無ければ結果を計算する。
  int bunsi[9], bunbo[9], sp = 0;
  for( int i=0; i< 9; i++) {
    if( cal[i] > 10) { // 数字なのでスタックに積む
      bunsi[sp] = cal[i];
      bunbo[sp] = 1;
      sp++;
    }
    else if( sp > 1) {          // 演算子なので計算する。但し数字が2つあることが前提。
      int op = cal[i];
      int m = bunsi[sp-2], n = bunbo[sp-2]; // m/n op t/s
      int t = bunsi[sp-1], s = bunbo[sp-1];
      if( op == 1/*+*/) { --sp; bunsi[sp-1] = m*s + n*t; bunbo[sp-1] = s*n;}
      if( op == 2/*−*/) { --sp; bunsi[sp-1] = m*s - n*t; bunbo[sp-1] = s*n;}
      if( op == 3/*×*/) { --sp; bunsi[sp-1] = m*t      ; bunbo[sp-1] = s*n;}
      if( op == 4/*÷*/) { --sp; bunsi[sp-1] = m*s      ; bunbo[sp-1] = n*t;}
    }
    else {
      // 計算不能
      return;
    }
  }
  if( bunsi[0] == bunbo[0]) {   // ( 分子 == 分母 ) ⇒ 1
    // 計算式を出力する。式は逆ポーランドのままなのでちょっと読みにくい。
    for( int i=0; i< 9; i++) {
      if( cal[i] > 10) ps( "%d  ", cal[i]);
      else             ps( "%s ", opstr[cal[i]]);
    }
    ps( "\n");
  }
}

int main( int argc, cstring argv[])
{
  for( int a=0; a< 9; a++) {
    used[a]++;
    for( int b=0; b< 9; b++) {
      if( used[b] >= nummax[b]) continue;
      used[b]++;
      for( int c=0; c< 9; c++) {
        if( used[c] >= nummax[c]) continue;
        used[c]++;
        for( int d=0; d< 9; d++) {
          if( used[d] >= nummax[d]) continue;
          used[d]++;
          for( int e=0; e< 9; e++) {
            if( used[e] >= nummax[e]) continue;
            used[e]++;
            for( int f=0; f< 9; f++) {
              if( used[f] >= nummax[f]) continue;
              used[f]++;
              for( int g=0; g< 9; g++) {
                if( used[g] >= nummax[g]) continue;
                used[g]++;
                for( int h=0; h< 9; h++) {
                  if( used[h] >= nummax[h]) continue;
                  used[h]++;
                  for( int i=0; i< 9; i++) {
                    if( used[i] >= nummax[i]) continue;
                    used[i]++;
                    // 演算式をスタックに積んで計算する。
                    cal[0] = numop[a];
                    cal[1] = numop[b];
                    cal[2] = numop[c];
                    cal[3] = numop[d];
                    cal[4] = numop[e];
                    cal[5] = numop[f];
                    cal[6] = numop[g];
                    cal[7] = numop[h];
                    cal[8] = numop[i];
                    // 実際に計算し結果が1になるものが解
                    calcRPN( cal);
                    used[i]--;
                  }
                  used[h]--;
                }
                used[g]--;
              }
              used[f]--;
            }
            used[e]--;
          }
          used[d]--;
        }
        used[c]--;
      }
      used[b]--;
    }
    used[a]--;
  }
  return    0;
}