朝日新聞2005年12月16日パズル横丁解答

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

25 55 + 25 x 5 + 
55 25 + 25 x 5 + 
1.187 秒

結果を普通の計算式に直すと以下のようになる。

( 22 + 55 ) x 25 + 5

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

#include "puzutl.h"

int             num[] = { 2, 5}; // 使える数
int             ope[] = { 0, 1/*Push*/, -1/*+*/, -2/*−*/, -3/*×*/, -4/*÷*/}; // 演算子, 0:何もしない,1:スタックに積む
int             used[] = {0,0}; // 数字を何個使ったか
int             mnum[] = { 2, 5}; // 2は2個,5は5個使える
int             mope = 3;       // 演算子使用最大数
int             nope = 0;       // 演算子数

void calcRPN( int cal[], int ical)
{
  // cal[] を逆ポーランド記法で数字と演算子が入っていると仮定し計算する。
  // エラーがあれば計算を打ち切り,エラーが無ければ結果を計算する。
  int bunsi[109], bunbo[109], sp = 0;
  for( int i=0; i< ical; i++) {
    if( cal[i] > 0) { // 数字なのでスタックに積む
      bunsi[sp] = cal[i];
      bunbo[sp] = 1;
      sp++;
    }
    else if( sp >= 2 && cal[i] < 0) {         // 演算子なので計算する。但し数字が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( bunbo[0] != 0 && (bunsi[0] / bunbo[0]) == 2005 && (bunsi[0] % bunbo[0]) == 0) { // 演算結果が2005になったら答えを出力
    char sope[] = { '@', '+', '-', 'x', '/'};
    for( int i=0; i< ical; i++) {
      if( cal[i] > 0) ps( "%d ", cal[i]);
      else if( cal[i] < 0) ps( "%c ", sope[-cal[i]]);
      else ps( "ERROR\n");
    }
    ps( "\n");
  }
}

void checkprint( int buf[]) {   // buf[]に入っている数字と演算子をスタックに積んで逆ポーランド記法と見なし計算する
  int           n = 0;
  int           ical = 0;
  int           cal[30];
  for( int i=0; i< 14; i++) {
    if( buf[i] == 1) {
      cal[ical++]   = n;        // 現在値をスタックに積む
      n = 0;
    }
    else if( buf[i] > 0) {
      n         *= 10;          // 数字は前の数字と合わせて新しい値になる
      n         += buf[i];
    }
    else if( buf[i] < 0) {      // 演算子が出現した
      cal[ical++]   = n;        // 演算子の前の数字をスタックに積み,
      cal[ical++]   = buf[i];   // 演算子もスタックに積む
      n = 0;                    // 
    }
  }
  calcRPN( cal, ical);
}
int main( int argc, cstring argv[])
{
  int buf[30];
  ProcTime pt; pt.start();
  for( int a=0; a< 2; a++) {
    used[a]++;
    buf[0] = num[a];
    for( int b=0; b< 6; b++) {
      if( ope[b] < 0 && nope >= mope) continue; if( ope[b] < 0) nope++;
      buf[1] = ope[b];
      for( int c=0; c< 2; c++) {
        if( used[c] >= mnum[c]) continue;
        used[c]++;
        buf[2] = num[c];
        for( int d=0; d< 6; d++) {
          if( ope[d] < 0 && nope >= mope) continue; if( ope[d] < 0) nope++;
          buf[3] = ope[d];
          for( int e=0; e< 2; e++) {
            if( used[e] >= mnum[e]) continue;
            used[e]++;
            buf[4] = num[e];
            for( int f=0; f< 6; f++) {
              if( ope[f] < 0 && nope >= mope) continue; if( ope[f] < 0) nope++;
              buf[5] = ope[f];
              for( int g=0; g< 2; g++) {
                if( used[g] >= mnum[g]) continue;
                used[g]++;
                buf[6] = num[g];
                for( int h=0; h< 6; h++) {
                  if( ope[h] < 0 && nope >= mope) continue; if( ope[h] < 0) nope++;
                  buf[7] = ope[h];
                  for( int i=0; i< 2; i++) {
                    if( used[i] >= mnum[i]) continue;
                    used[i]++;
                    buf[8] = num[i];
                    for( int j=0; j< 6; j++) {
                      if( ope[j] < 0 && nope >= mope) continue; if( ope[j] < 0) nope++;
                      buf[9] = ope[j];
                      for( int k=0; k< 2; k++) {
                        if( used[k] >= mnum[k]) continue;
                        used[k]++;
                        buf[10] = num[k];
                        for( int l=0; l< 6; l++) {
                          if( ope[l] < 0 && nope >= mope) continue; if( ope[l] < 0) nope++;
                          buf[11] = ope[l];
                          for( int m=0; m< 2; m++) {
                            if( used[m] >= mnum[m]) continue;
                            used[m]++;
                            buf[12] = num[m];
                            for( int n=0; n< 6; n++) {
                              if( ope[n] < 0 && nope >= mope) continue; if( ope[n] < 0) nope++;
                              buf[13] = ope[n];
                              if( nope != 0) {
                                checkprint( buf); // 演算子は一つは無いとダメ
                              }
                              if( ope[n] < 0) nope--;
                            }
                            used[m]--;
                          }
                          if( ope[l] < 0) nope--;
                        }
                        used[k]--;
                      }
                      if( ope[j] < 0) nope--;
                    }
                    used[i]--;
                  }
                  if( ope[h] < 0) nope--;
                }
                used[g]--;
              }
              if( ope[f] < 0) nope--;
            }
            used[e]--;
          }
          if( ope[d] < 0) nope--;
        }
        used[c]--;
      }
      if( ope[b] < 0) nope--;
    }
    used[a]--;
  }
  pt.end();
  ps( "%g 秒\n", pt.sec());
  return    0;
}