朝日新聞2007年3月9日パズル横丁解答

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

Find
4x4 順序は:DECAB
. E E E C . . 
. E E E C . . 
. D D B C . . 
. D D B A . . 
. . . . . . . 
. . . . . . . 
. . . . . . . 
5x5 順序は:DECAB
. . . . . . . 
. . . . . . . 
C C C E E . . 
A B B E E . . 
. . . E E . . 
. . . D D . . 
. . . D D . . 
2.437 秒

判りやすい図にすると以下の通り。

ちょっと長くて汚いけどプログラムのソースは以下の通り。

#include "puzutl.h"

const ulong bit0  = 0x00000001;
const ulong bit1  = 0x00000002;
const ulong bit2  = 0x00000004;
const ulong bit3  = 0x00000008;
const ulong bit4  = 0x00000010;
const ulong bit5  = 0x00000020;
const ulong bit6  = 0x00000040;
const ulong bit7  = 0x00000080;
const ulong bit8  = 0x00000100;
const ulong bit9  = 0x00000200;
const ulong bit10 = 0x00000400;
const ulong bit11 = 0x00000800;
const ulong bit12 = 0x00001000;
const ulong bit13 = 0x00002000;
const ulong bit14 = 0x00004000;
const ulong bit15 = 0x00008000;
const ulong bit16 = 0x00010000;
const ulong bit17 = 0x00020000;
const ulong bit18 = 0x00040000;
const ulong bit19 = 0x00080000;
const ulong bit20 = 0x00100000;
const ulong bit21 = 0x00200000;
const ulong bit22 = 0x00400000;
const ulong bit23 = 0x00800000;
const ulong bit24 = 0x01000000;
const ulong bit25 = 0x02000000;
const ulong bit26 = 0x04000000;
const ulong bit27 = 0x08000000;
const ulong bit28 = 0x10000000;
const ulong bit29 = 0x20000000;
const ulong bit30 = 0x40000000;
const ulong bit31 = 0x80000000;
const int64 bit32 = 0x0000000100000000L;
const int64 bit33 = 0x0000000200000000L;
const int64 bit34 = 0x0000000400000000L;
const int64 bit35 = 0x0000000800000000L;
const int64 bit36 = 0x0000001000000000L;
const int64 bit37 = 0x0000002000000000L;
const int64 bit38 = 0x0000004000000000L;
const int64 bit39 = 0x0000008000000000L;
const int64 bit40 = 0x0000010000000000L;
const int64 bit41 = 0x0000020000000000L;
const int64 bit42 = 0x0000040000000000L;
const int64 bit43 = 0x0000080000000000L;
const int64 bit44 = 0x0000100000000000L;
const int64 bit45 = 0x0000200000000000L;
const int64 bit46 = 0x0000400000000000L;
const int64 bit47 = 0x0000800000000000L;
const int64 bit48 = 0x0001000000000000L;
const int64 bit49 = 0x0002000000000000L;
const int64 bit50 = 0x0004000000000000L;
const int64 bit51 = 0x0008000000000000L;
const int64 bit52 = 0x0010000000000000L;
const int64 bit53 = 0x0020000000000000L;
const int64 bit54 = 0x0040000000000000L;
const int64 bit55 = 0x0080000000000000L;
const int64 bit56 = 0x0100000000000000L;
const int64 bit57 = 0x0200000000000000L;
const int64 bit58 = 0x0400000000000000L;
const int64 bit59 = 0x0800000000000000L;
const int64 bit60 = 0x1000000000000000L;
const int64 bit61 = 0x2000000000000000L;
const int64 bit62 = 0x4000000000000000L;
const int64 bit63 = 0x8000000000000000L;

inline int64 normalize_7x7( int64 v) // 7x7の図形を正規化する(左上に図形を移動する)
{
  if( !v) return 0;             // 永久ループ防止
  while( !(v & (bit0|bit1|bit2|bit3|bit4|bit5|bit6))) {
    v >>= 7;
  }
  while( !(v & (bit0|bit7|bit14|bit21|bit28|bit35|bit42))) {
    v = ((v&(bit1|bit2|bit3|bit4|bit5|bit6))>>1)
      | ((v&(bit8|bit9|bit10|bit11|bit12|bit13))>>1)
      | ((v&(bit15|bit16|bit17|bit18|bit19|bit20))>>1)
      | ((v&(bit22|bit23|bit24|bit25|bit26|bit27))>>1)
      | ((v&(bit29|bit30|bit31|bit32|bit33|bit34))>>1)
      | ((v&(bit36|bit37|bit38|bit39|bit40|bit41))>>1)
      | ((v&(bit43|bit44|bit45|bit46|bit47|bit48))>>1)
      ;
  }
  return    v;
}

inline int height_7x7( int64 v) // 7x7の図形の高さを取得する
{
  int           rowEnd = 0;
  if(      v & (bit42|bit43|bit44|bit45|bit46|bit47|bit48)) rowEnd = 7;
  else if( v & (bit35|bit36|bit37|bit38|bit39|bit40|bit41)) rowEnd = 6;
  else if( v & (bit28|bit29|bit30|bit31|bit32|bit33|bit34)) rowEnd = 5;
  else if( v & (bit21|bit22|bit23|bit24|bit25|bit26|bit27)) rowEnd = 4;
  else if( v & (bit14|bit15|bit16|bit17|bit18|bit19|bit20)) rowEnd = 3;
  else if( v & (bit7 |bit8 |bit9 |bit10|bit11|bit12|bit13)) rowEnd = 2;
  else if( v & (bit0 |bit1 |bit2 |bit3 |bit4 |bit5 |bit6 )) rowEnd = 1;
  return    rowEnd;
}

inline int width_7x7( int64 v)  // 7x7の図形の幅を取得する
{
  int           colEnd = 0;
  if(      v & (bit6 |bit13|bit20|bit27|bit34|bit41|bit48)) colEnd = 7;
  else if( v & (bit5 |bit12|bit19|bit26|bit33|bit40|bit47)) colEnd = 6;
  else if( v & (bit4 |bit11|bit18|bit25|bit32|bit39|bit46)) colEnd = 5;
  else if( v & (bit3 |bit10|bit17|bit24|bit31|bit38|bit45)) colEnd = 4;
  else if( v & (bit2 |bit9 |bit16|bit23|bit30|bit37|bit44)) colEnd = 3;
  else if( v & (bit1 |bit8 |bit15|bit22|bit29|bit36|bit43)) colEnd = 2;
  else if( v & (bit0 |bit7 |bit14|bit21|bit28|bit35|bit42)) colEnd = 1;
  return    colEnd;
}

YesNo is_7x7_square_4x4( int64 A) // 7x7の図形の中の4x4の正方形か?
{
  YesNo         yn_square = NO, yn_rectangle = NO;
  A             = normalize_7x7(A);
  int           nbits = bitcount(A);
  int           height = height_7x7(A);
  int           width = width_7x7(A);
  yn_square     = false;
  if( nbits != 4*4) return NO;
  if( height*width == nbits) yn_rectangle = YES;
  if( yn_rectangle && height == width) yn_square = YES;
  return    yn_square;
}

// 以上汎用部分

string          finded_msg;     // 見つかった図形

void dump_7x7( cstring msg, int64 A, int64 B, int64 C, int64 D, int64 E, char cA, char cB, char cC, char cD, char cE)
{
  char          cs[2048];
  char          buf[7][7];
  int           row, col;
  char          defC = '.';
  for( row=0; row< 7; row++) {
    for( col=0; col< 7; col++) {
      buf[row][col] = defC;
      if( A&1) { if(buf[row][col]==defC) buf[row][col] = cA; else buf[row][col]='?';}
      if( B&1) { if(buf[row][col]==defC) buf[row][col] = cB; else buf[row][col]='?';}
      if( C&1) { if(buf[row][col]==defC) buf[row][col] = cC; else buf[row][col]='?';}
      if( D&1) { if(buf[row][col]==defC) buf[row][col] = cD; else buf[row][col]='?';}
      if( E&1) { if(buf[row][col]==defC) buf[row][col] = cE; else buf[row][col]='?';}
      A         >>= 1;
      B         >>= 1;
      C         >>= 1;
      D         >>= 1;
      E         >>= 1;
    }
  }
  sprintf( cs, "%s 順序は:%c%c%c%c%c\n", msg, cA, cB, cC, cD, cE); finded_msg += cs;
  for( row=0; row< 7; row++) {
    for( col=0; col< 7; col++) {
      sprintf( cs, "%c ", buf[row][col]); finded_msg += cs;
    }
    sprintf( cs, "\n"); finded_msg += cs;
  }
}

class rect {                    // 矩形
public:
  rect( char c, int x, int y);  // 矩形に文字と大きさを割り当てる
  rect( const rect& r);         // コピー
  void mirror();                // MIRRORする,正方形でもMIRRORする
  YesNo end_of_mirror();        // MIRROR出来たか?
  void rotate90();              // 左に90度回転
  int64 move( int& x, int& y);  // (x,y)から矩形分座標位置を移動する,戻り値は図形のビット表現
  char getchr() { return m_c;}  // 矩形文字を得る(ダンプ出力用)

private:
  char          m_c;            // 矩形文字('A','B','C','D','E','F'の何れか)
  int           m_x;            // 座標位置
  int           m_y;            // 
  YesNo         m_yn_square;    // 正方形か?
  int           m_mirror_count; // 正方形でない場合MIRRORした回数(初期値:0,最大値:1)
};

rect::rect( char c, int x, int y) // 座標位置を割り当てる
{
  m_c           = c;
  m_x           = x;
  m_y           = y;
  m_yn_square   = YES;
  if( abs(m_x) != abs(m_y)) m_yn_square = NO; // x=yでないので正方形でない
  m_mirror_count= 0;
}

rect::rect( const rect& r)      // コピー
{
  m_c           = r.m_c;
  m_x           = r.m_x;
  m_y           = r.m_y;
  m_yn_square   = YES;
  if( abs(m_x) != abs(m_y)) m_yn_square = NO;
  m_mirror_count= 0;
}

void rect::mirror()
{
  m_y           = -m_y;
  m_mirror_count++;
}

YesNo rect::end_of_mirror()
{
  if( m_yn_square && m_mirror_count >= 1) return YES; // 正方形の場合mirrorは必要ない
  if( m_mirror_count >= 2) return YES; // 長方形の場合は1回有効
  return    NO;
}

void rect::rotate90()           // 90度左回転
{
  int           T = m_x;
  m_x           = -m_y;
  m_y           = T;
}

int64 get7x7bit( int x, int y, int dx, int dy)  // (x,y)-(x+dx,y+dy)の図形を7x7のbitで取得する
{
  x             += dx;
  y             += dy;
  if( x < 0) return 0;
  if( y < 0) return 0;
  if( x >= 7) return 0;
  if( y >= 7) return 0;
  y             = 7-y-1;
  int64         bit = 1;
  bit           <<= y*7;
  bit           <<= x;
  return    bit;
}

int64 rect::move( int& org_x, int& org_y)
{
  int           start_x = org_x;
  int           start_y = org_y;
  if( m_x < 0) start_x += m_x;
  if( m_y < 0) start_y += m_y;
  org_x         += m_x;
  org_y         += m_y;
  if( org_x < 0 || org_x > 7 ||
      org_y < 0 || org_y > 7) return 0; // 7x7の図形をはみ出た
  int64         plane = 0;
  int           X = m_x;
  int           Y = m_y;
  if( X < 0) { X = -X; }
  if( Y < 0) { Y = -Y; }
  for( int x=0; x< X; x++) {
    for( int y=0; y< Y; y++) {
      plane     |= get7x7bit( start_x, start_y, x, y);
    }
  }
  return    plane;
}

rect R[6] = {
  rect('A',1,1),
  rect('B',2,1),
  rect('C',3,1),
  rect('D',2,2),
  rect('E',3,2),
  rect('F',3,3),
};

int64           plane4x4_03 = bit0|bit1|bit2|bit3|bit7|bit8|bit9|bit10|bit14|bit15|bit16|bit17|bit21|bit22|bit23|bit24;
int64           plane4x4_33 = bit3|bit4|bit5|bit6|bit10|bit11|bit12|bit13|bit17|bit18|bit19|bit20|bit24|bit25|bit26|bit27;
int64           plane4x4_30 = bit24|bit25|bit26|bit27|bit31|bit32|bit33|bit34|bit38|bit39|bit40|bit41|bit45|bit46|bit47|bit48;
int64           plane5x5 = bit14|bit15|bit16|bit17|bit18|bit21|bit22|bit23|bit24|bit25|bit31|bit32|bit38|bit39|bit45|bit46;
int64           planeF = bit28|bit29|bit30|bit35|bit36|bit37|bit42|bit43|bit44;

YesNo check4x4_ok( int start_x, int start_y, rect A, rect B, rect C, rect D, rect E) // 4x4の矩形が出来てFと重ならないか?
{
  int           x = start_x, y = start_y;
  int64         planeA, planeB, planeC, planeD, planeE;
  planeA        = A.move(x,y);
  planeB        = B.move(x,y);
  planeC        = C.move(x,y);
  planeD        = D.move(x,y);
  planeE        = E.move(x,y);
  if( !planeA |!planeB |!planeC |!planeD |!planeE) return NO;
  if( (planeA | planeB | planeC | planeD | planeE) & planeF) return NO;
  if( is_7x7_square_4x4( planeA | planeB | planeC | planeD | planeE)) {
    dump_7x7( "4x4", planeA, planeB, planeC, planeD, planeE, A.getchr(), B.getchr(), C.getchr(), D.getchr(), E.getchr());
    return    YES;
  }
  return    NO;
}

YesNo check4x4_with_rotate( int start_x, int start_y, rect A, rect B, rect C, rect D, rect E) // 回転を考慮する
{
  for( int rotateA=0; rotateA< 4; rotateA++) {
    A.rotate90();
    for( int rotateB=0; rotateB< 4; rotateB++) {
      B.rotate90();
      for( int rotateC=0; rotateC< 4; rotateC++) {
        C.rotate90();
        for( int rotateD=0; rotateD< 4; rotateD++) {
          D.rotate90();
          for( int rotateE=0; rotateE< 4; rotateE++) {
            E.rotate90();
            if( check4x4_ok( start_x, start_y, A,B,C,D,E)) return YES;
          }
        }
      }
    }
  }
  return    NO;
}

YesNo check4x4( int start_x, int start_y, rect start_A, rect start_B, rect start_C, rect start_D, rect start_E) // 4x4の矩形が出来るか?
{
  for( rect A(start_A); !A.end_of_mirror(); A.mirror()) {
    for( rect B(start_B); !B.end_of_mirror(); B.mirror()) {
      for( rect C(start_C); !C.end_of_mirror(); C.mirror()) {
        for( rect D(start_D); !D.end_of_mirror(); D.mirror()) {
          for( rect E(start_E); !E.end_of_mirror(); E.mirror()) {
            if( check4x4_with_rotate( start_x, start_y, A,B,C,D,E)) {
              return    YES;
            }
          }
        }
      }
    }
  }
  return    NO;
}

YesNo check5x5_ok( int start_x, int start_y, int64 plane, rect A, rect B, rect C, rect D, rect E)
{
  int           x = start_x, y = start_y;
  int64         planeA, planeB, planeC, planeD, planeE;
  planeA        = A.move(x,y);
  planeB        = B.move(x,y);
  planeC        = C.move(x,y);
  planeD        = D.move(x,y);
  planeE        = E.move(x,y);
  if( ( planeA | planeB | planeC | planeD | planeE ) == plane) {
    dump_7x7( "5x5", planeA, planeB, planeC, planeD, planeE, A.getchr(), B.getchr(), C.getchr(), D.getchr(), E.getchr());
    return    YES;
  }
  return    NO;
}

YesNo check5x5_with_rotate( int start_x, int start_y, int64 plane, rect A, rect B, rect C, rect D, rect E)
{
  for( int rotateA=0; rotateA< 4; rotateA++) {
    A.rotate90();
    for( int rotateB=0; rotateB< 4; rotateB++) {
      B.rotate90();
      for( int rotateC=0; rotateC< 4; rotateC++) {
        C.rotate90();
        for( int rotateD=0; rotateD< 4; rotateD++) {
          D.rotate90();
          for( int rotateE=0; rotateE< 4; rotateE++) {
            E.rotate90();
            if( check5x5_ok( start_x, start_y, plane, A,B,C,D,E)) return YES;
          }
        }
      }
    }
  }
  return    NO;
}

YesNo check5x5( int start_x, int start_y, int64 plane, rect start_A, rect start_B, rect start_C, rect start_D, rect start_E)
{
  for( rect A(start_A); !A.end_of_mirror(); A.mirror()) {
    for( rect B(start_B); !B.end_of_mirror(); B.mirror()) {
      for( rect C(start_C); !C.end_of_mirror(); C.mirror()) {
        for( rect D(start_D); !D.end_of_mirror(); D.mirror()) {
          for( rect E(start_E); !E.end_of_mirror(); E.mirror()) {
            if( check5x5_with_rotate( start_x, start_y, plane, A,B,C,D,E)) {
              return    YES;
            }
          }
        }
      }
    }
  }
  return    NO;
}

void test(rect A, YesNo yn_mirror, int num_rotate)
{
  if( yn_mirror) A.mirror();
  for( int i=0; i< num_rotate; i++) {
    A.rotate90();
  }
  int x = 3, y = 3;
  int64 planeA = A.move(x,y);
  dump_7x7( "dump", planeA, 0, 0, 0, 0, 'A', 0, 0, 0, 0);
}

YesNo used[6];

int main( int argc, cstring argv[])
{
  ProcTime      pt;pt.start();
  for( int a=0; a< 5; a++) {
    used[a] = YES;
    for( int b=0; b< 5; b++) {
      if( used[b]) continue;
      used[b] = YES;
      for( int c=0; c< 5; c++) {
        if( used[c]) continue;
        used[c] = YES;
        for( int d=0; d< 5; d++) {
          if( used[d]) continue;
          used[d] = YES;
          for( int e=0; e< 5; e++) {
            if( used[e]) continue;
            used[e] = YES;
            finded_msg  = "";
            if( check4x4( 3,0,R[a],R[b],R[c],R[d],R[e]) ||
                check4x4( 3,3,R[a],R[b],R[c],R[d],R[e]) ||
                check4x4( 0,3,R[a],R[b],R[c],R[d],R[e])) {
              if( check5x5( 3,0,plane5x5,R[a],R[b],R[c],R[d],R[e])) { ps( "Find\n%s", finded_msg.c_str());}//{ps( "Find5x5-3,0  a:%d,b:%d,c:%d,d:%d,e:%d\n",a,b,c,d,e);}
              else if( check5x5( 3,3,plane5x5,R[a],R[b],R[c],R[d],R[e])) { ps( "Find\n%s", finded_msg.c_str());}//{ps( "Find5x5-3,3  a:%d,b:%d,c:%d,d:%d,e:%d\n",a,b,c,d,e);}
              else if( check5x5( 0,3,plane5x5,R[a],R[b],R[c],R[d],R[e])) { ps( "Find\n%s", finded_msg.c_str());}//{ps( "Find5x5-0,3  a:%d,b:%d,c:%d,d:%d,e:%d\n",a,b,c,d,e);}
            }
            used[e] = NO;
          }
          used[d] = NO;
        }
        used[c] = NO;
      }
      used[b] = NO;
    }
    used[a] = NO;
  }
  pt.end();
  ps( "%g 秒\n", pt.sec());
  return    0;
}