朝日新聞2007年1月12日パズル横丁解答

プログラムの実行結果は以下の通り。3個目と4個目が答え。

+--+--+--+
| 2| 7| 6|
+--+--+--+
| 9| 5| 1|
+--+--+--+
| 4| 3| 8|
+--+--+--+

+--+--+--+
|11|16|15|
+--+--+--+
|18|14|10|
+--+--+--+
|13|12|17|
+--+--+--+

+--+--+--+
| 2|13| 9|
+--+--+--+
|15| 8| 1|
+--+--+--+
| 7| 3|14|
+--+--+--+

+--+--+--+
| 5|16|12|
+--+--+--+
|18|11| 4|
+--+--+--+
|10| 6|17|
+--+--+--+

+--+--+--+
| 3|13|11|
+--+--+--+
|17| 9| 1|
+--+--+--+
| 7| 5|15|
+--+--+--+

+--+--+--+
| 4|14|12|
+--+--+--+
|18|10| 2|
+--+--+--+
| 8| 6|16|
+--+--+--+

0.343秒

文章にすると,「2倍して元の数を3で割った余りに1足したものを引く。もう一つは,それに+3したもの」。

わかり辛い。式だと「2*x-mod(x,3)-1」。

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

#include "puzutl.h"

YesNo           used[18+1];     // 1〜18の数字を使ったか? 1オリジンで使用する

int ga,gb,gc,gd,ge,gf,gg,gh,gi,gj; // 魔方陣を見つける関数を使い廻すので最初の魔方陣を見つけたら記録しておく
set<string>     finded;         // 見つけた魔方陣

void rotate90( int mat[3][3])   // 左に90度回転
{
  // abc => cfi
  // def    beh
  // ghi    adg
  int           a=mat[0][0];
  int           b=mat[0][1];
  int           c=mat[0][2];
  int           d=mat[1][0];
  int           e=mat[1][1];
  int           f=mat[1][2];
  int           g=mat[2][0];
  int           h=mat[2][1];
  int           i=mat[2][2];
  mat[0][0]     = c;
  mat[0][1]     = f;
  mat[0][2]     = i;
  mat[1][0]     = b;
  mat[1][1]     = e;
  mat[1][2]     = h;
  mat[2][0]     = a;
  mat[2][1]     = d;
  mat[2][2]     = g;
}

void mirror( int mat[3][3])     // 左右対称
{
  // abc => cba
  // def    fed
  // ghi    ihg
  int           a=mat[0][0];
  int           b=mat[0][1];
  int           c=mat[0][2];
  int           d=mat[1][0];
  int           e=mat[1][1];
  int           f=mat[1][2];
  int           g=mat[2][0];
  int           h=mat[2][1];
  int           i=mat[2][2];
  mat[0][0]     = c;
  mat[0][1]     = b;
  mat[0][2]     = a;
  mat[1][0]     = f;
  mat[1][1]     = e;
  mat[1][2]     = d;
  mat[2][0]     = i;
  mat[2][1]     = h;
  mat[2][2]     = g;
}

cstring mat2str( int mat[3][3])
{
  static char buf[128];
  buf[0]        = 'A' + mat[0][0]; // 1数字1文字で記録できるように数字でなくアルファベットで記録する
  buf[1]        = 'A' + mat[0][1]; // 'A'から使う必要も無いので -1 しないでよい
  buf[2]        = 'A' + mat[0][2]; // 結局'B'から18文字使う
  buf[3]        = 'A' + mat[1][0];
  buf[4]        = 'A' + mat[1][1];
  buf[5]        = 'A' + mat[1][2];
  buf[6]        = 'A' + mat[2][0];
  buf[7]        = 'A' + mat[2][1];
  buf[8]        = 'A' + mat[2][2];
  buf[9]        = NIL;          // 文字列にして
  return    buf;
}

void print( int mat[3][3])      // 魔方陣を出力する
{
  cstring       s = mat2str( mat);
  if( finded.find(s) == finded.end()) {
    ps( "+--+--+--+\n");
    ps( "|%2d|%2d|%2d|\n", mat[0][0], mat[0][1], mat[0][2]);
    ps( "+--+--+--+\n");
    ps( "|%2d|%2d|%2d|\n", mat[1][0], mat[1][1], mat[1][2]);
    ps( "+--+--+--+\n");
    ps( "|%2d|%2d|%2d|\n", mat[2][0], mat[2][1], mat[2][2]);
    ps( "+--+--+--+\n\n");
    finded.insert(s);
  }
}

void regist( int mat[3][3]) // 重複出力を避けるために見つかった魔方陣を記録する
{
  cstring       s = mat2str( mat);
  finded.insert( s);        // 登録する,既に登録してあれば登録できない
}

void print_find( int a, int b, int c, int d, int e, int f, int g, int h, int i) // 見つかった魔方陣を出力する
{
  int mat[3][3] = {a,b,c,d,e,f,g,h,i}; // 見つかった魔方陣
  print(mat);                   // 見つかった魔方陣を出力する,関数の中で重複チェックする
  // 重複を出力しないように見つかった魔方陣と回転,ミラーしたものも登録しておく
  regist(mat); mirror(mat); regist(mat); mirror(mat); rotate90(mat); 
  regist(mat); mirror(mat); regist(mat); mirror(mat); rotate90(mat); 
  regist(mat); mirror(mat); regist(mat); mirror(mat); rotate90(mat); 
  regist(mat); mirror(mat); regist(mat); mirror(mat); rotate90(mat); 
}

void magic3( int once)
{
  // abc
  // def
  // ghi
  int           abc;
  for( int a=1; a<= 18; a++) {  // 1〜9でなく1〜18の数字を使う魔方陣
    if( used[a]) continue;      // 一個だけの時は必要ないが関数を2回使い廻すので
    used[a] = YES;
    for( int b=1; b<= 18; b++) {
      if( used[b]) continue;
      used[b] = YES;
      for( int c=1; c<= 18; c++) {
        if( used[c]) continue;
        abc     = a+b+c;        // 最初の行は何の制約もなく作成できる
        used[c] = YES;
        for( int d=1; d<= 18; d++) {
          if( used[d]) continue;
          if( a+d >= abc || a+d+18 < abc) continue;
          used[d] = YES;
          for( int e=1; e<= 18; e++) {
            if( used[e]) continue;
            if( b+e >= abc || b+e+18 < abc) continue;
            if( a+e >= abc || a+e+18 < abc) continue;
            if( c+e >= abc || c+e+18 < abc) continue;
            used[e] = YES;
            for( int f=1; f<= 18; f++) {
              if( used[f]) continue;
              if( d+e+f != abc) continue;
              if( c+f >= abc || c+f+18 < abc) continue;
              used[f] = YES;
              for( int g=1; g<= 18; g++) {
                if( used[g]) continue;
                if( a+d+g != abc) continue;
                if( c+e+g != abc) continue;
                used[g] = YES;
                for( int h=1; h<= 18; h++) {
                  if( used[h]) continue;
                  if( b+e+h != abc) continue;
                  used[h] = YES;
                  for( int i=1; i<= 18; i++) {
                    if( used[i]) continue;
                    if( g+h+i != abc) continue;
                    if( c+f+i != abc) continue;
                    if( a+e+i != abc) continue;
                    used[i] = YES;
                    if( once) { // 最初の魔方陣が見つかった
                      ga=a,gb=b,gc=c,gd=d,ge=e,gf=f,gg=g,gh=h,gi=i;// 解答を出力するため記録しておく
                      magic3( 0); // 2個目の魔方陣を探す
                    }
                    else {      // 2個目の魔方陣が見つかった
                      print_find( ga,gb,gc,gd,ge,gf,gg,gh,gi); // 1個目の魔方陣
                      print_find( a,b,c,d,e,f,g,h,i); // 2個目の魔方陣
                    }
                    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;
  }
}

int main( int argc, cstring argv[])
{
  ProcTime      pt;pt.start();
  magic3( 1);
  pt.end();
  ps( "%g秒\n", pt.sec());
  return    0;
}