朝日新聞2007年2月16日パズル横丁解答

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

find[1]
6x6 Layout:
C C C C C C  
C C C C C C  
x . . . . .  
x . . . . .  
x . . . . .  
x . . . . .  
       
7x7 Layout:
. . . . . C C 
. . . . . C C 
. . . . . C C 
. . . . . C C 
A A A B B C C 
A A A B B C C 
A A A x x x x 

0.125秒

図にすると以下の通り。

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

#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 shift_right( int64 v)
{
  if( (v&bit6) || (v&bit13) || (v&bit20) || (v&bit27) || (v&bit34) || (v&bit41) || (v&bit48)) return 0;
  return    v<<1;
}

inline int64 shift_down( int64 v)
{
  if( (v&bit42) || (v&bit43) || (v&bit44) || (v&bit45) || (v&bit46) || (v&bit47) || (v&bit48)) return 0;
  return    v<<7;
}

inline int64 shift( int64 v, int x, int y) // 正規化した位置からx,y移動する
{
  while( x-->0) v = shift_right(v);
  while( y-->0) v = shift_down(v);
  return v;
}

inline int64 normalize( int64 v) // 正規化する(左上に図形を移動する)
{
  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;
}

int64 rotate90( int64 x) {      // 90度回転する
  int64 v = 0;
  if( x&bit0) v |= bit42;
  if( x&bit1) v |= bit35;
  if( x&bit2) v |= bit28;
  if( x&bit3) v |= bit21;
  if( x&bit4) v |= bit14;
  if( x&bit5) v |= bit7;
  if( x&bit6) v |= bit0;
  if( x&bit7) v |= bit43;
  if( x&bit8) v |= bit36;
  if( x&bit9) v |= bit29;
  if( x&bit10) v |= bit22;
  if( x&bit11) v |= bit15;
  if( x&bit12) v |= bit8;
  if( x&bit13) v |= bit1;
  if( x&bit14) v |= bit44;
  if( x&bit15) v |= bit37;
  if( x&bit16) v |= bit30;
  if( x&bit17) v |= bit23;
  if( x&bit18) v |= bit16;
  if( x&bit19) v |= bit9;
  if( x&bit20) v |= bit2;
  if( x&bit21) v |= bit45;
  if( x&bit22) v |= bit38;
  if( x&bit23) v |= bit31;
  if( x&bit24) v |= bit24;
  if( x&bit25) v |= bit17;
  if( x&bit26) v |= bit10;
  if( x&bit27) v |= bit3;
  if( x&bit28) v |= bit46;
  if( x&bit29) v |= bit39;
  if( x&bit30) v |= bit32;
  if( x&bit31) v |= bit25;
  if( x&bit32) v |= bit18;
  if( x&bit33) v |= bit11;
  if( x&bit34) v |= bit4;
  if( x&bit35) v |= bit47;
  if( x&bit36) v |= bit40;
  if( x&bit37) v |= bit33;
  if( x&bit38) v |= bit26;
  if( x&bit39) v |= bit19;
  if( x&bit40) v |= bit12;
  if( x&bit41) v |= bit5;
  if( x&bit42) v |= bit48;
  if( x&bit43) v |= bit41;
  if( x&bit44) v |= bit34;
  if( x&bit45) v |= bit27;
  if( x&bit46) v |= bit20;
  if( x&bit47) v |= bit13;
  if( x&bit48) v |= bit6;
  return v;
}

int64 mirror( int64 x) {
  int64 v = 0;
  if( x&bit0) v |= bit6;
  if( x&bit1) v |= bit5;
  if( x&bit2) v |= bit4;
  if( x&bit3) v |= bit3;
  if( x&bit4) v |= bit2;
  if( x&bit5) v |= bit1;
  if( x&bit6) v |= bit0;
  if( x&bit7) v |= bit13;
  if( x&bit8) v |= bit12;
  if( x&bit9) v |= bit11;
  if( x&bit10) v |= bit10;
  if( x&bit11) v |= bit9;
  if( x&bit12) v |= bit8;
  if( x&bit13) v |= bit7;
  if( x&bit14) v |= bit20;
  if( x&bit15) v |= bit19;
  if( x&bit16) v |= bit18;
  if( x&bit17) v |= bit17;
  if( x&bit18) v |= bit16;
  if( x&bit19) v |= bit15;
  if( x&bit20) v |= bit14;
  if( x&bit21) v |= bit27;
  if( x&bit22) v |= bit26;
  if( x&bit23) v |= bit25;
  if( x&bit24) v |= bit24;
  if( x&bit25) v |= bit23;
  if( x&bit26) v |= bit22;
  if( x&bit27) v |= bit21;
  if( x&bit28) v |= bit34;
  if( x&bit29) v |= bit33;
  if( x&bit30) v |= bit32;
  if( x&bit31) v |= bit31;
  if( x&bit32) v |= bit30;
  if( x&bit33) v |= bit29;
  if( x&bit34) v |= bit28;
  if( x&bit35) v |= bit41;
  if( x&bit36) v |= bit40;
  if( x&bit37) v |= bit39;
  if( x&bit38) v |= bit38;
  if( x&bit39) v |= bit37;
  if( x&bit40) v |= bit36;
  if( x&bit41) v |= bit35;
  if( x&bit42) v |= bit48;
  if( x&bit43) v |= bit47;
  if( x&bit44) v |= bit46;
  if( x&bit45) v |= bit45;
  if( x&bit46) v |= bit44;
  if( x&bit47) v |= bit43;
  if( x&bit48) v |= bit42;
  return    v;
}

int64 getbit( int i)
{
  int64 bits[] = { bit0, bit1, bit2, bit3, bit4, bit5, bit6, bit7, bit8, bit9, bit10, bit11, bit12, bit13, bit14, bit15, bit16, bit17, bit18, bit19, bit20, bit21, bit22, bit23, bit24, bit25, bit26, bit27, bit28, bit29, bit30, bit31, bit32, bit33, bit34, bit35, bit36, bit37, bit38, bit39, bit40, bit41, bit42, bit43, bit44, bit45, bit46, bit47, bit48, bit49, bit50, bit51, bit52, bit53, bit54, bit55, bit56, bit57, bit58, bit59, bit60, bit61, bit62, bit63};
  assert( i>= 0 && i< 64);
  return    bits[i];
}

int bitcountcmp( const void* v1, const void* v2)
{
  int64         c1 = *(int64*)v1;
  int64         c2 = *(int64*)v2;
  return    bitcount(c2) - bitcount(c1);
}

void cut66( int row, int col, int64& c1, int64& c2, int64& c3, int64& orgC1, int64& orgC2, int64& orgC3) // 6x6の図形Cを3分割する
{
  c1 = c2 = c3 = 0;
  for( int i=0; i< row; i++) {  // 横に入れる切り込み線の上がc1
    for( int j=0; j< 6; j++) {
      c1        |= getbit(i*7+j);
    }
  }
  for( ; i< 6; i++) {           // 横の切り込み線の下にc2,c3がある。
    for( int j=0; j< col; j++) { // 縦の切り込み線の左がc2
      c2        |= getbit(i*7+j);
    }
    for( ; j< 6; j++) {         // 縦の切り込み線の右がc3
      c3        |= getbit(i*7+j);
    }
  }
  int64         c[3] = {c1,c2,c3};
  qsort( c, 3, sizeof(int64), bitcountcmp); // ビット数順にソートして返す,一番大きな図形を7x7の左上に配置するため
  c1            = normalize(c[0]);
  c2            = normalize(c[1]);
  c3            = normalize(c[2]);
  orgC1         = c[0];         // 元の図形のレイアウトを表示するために使用する
  orgC2         = c[1];
  orgC3         = c[2];
}

YesNo layout( int64& mat, int64 v, int64& shiftV) // 左上から空いている場所を探しvを配置する。
{
  for( int row=0; row< 7; row++) {
    for( int col=0; col< 7; col++) {
      if( (mat&getbit(row*7+col)) == 0) { // 空きが見つかった,ここにvを配置する
        shiftV = shift(v,col,row);
        if( shiftV == 0) return    NO; // 7x7の矩形を溢れた
        mat     |= shiftV;
        return    YES;
      }
    }
  }
  assert( 0);                   // ここに来ることは無いはず
  return        NO;             // あり得ない
}

set<string> goal;               // 見つかった解答を重複出力しないようにする
int num_goal = 0;               // 重複除去した結果の解答数

void pack_goal( astring p, int64 A, char a, int64 B, char b, int64 C, char c, int64 D, char d, int64 E, char e) // 7x7の矩形を文字列に変換する
{
  // 出力用と重複チェックの両方で使う。
  YesNo         yn_printed;
  for( int i=0; i< 49; i++) {
    yn_printed  = NO;
    if( A  & 1) { *p++ = a; yn_printed = YES;}
    if( B  & 1) { *p++ = b; yn_printed = YES;}
    if( C  & 1) { *p++ = c; yn_printed = YES;}
    if( D  & 1) { *p++ = d; yn_printed = YES;}
    if( E  & 1) { *p++ = e; yn_printed = YES;}
    *p++= ' ';
    if( ((i+1)%7)==0) *p++ = '\n';
    A>>=1;
    B>>=1;
    C>>=1;
    D>>=1;
    E>>=1;
  }
  *p = NIL;
}

void regist_goal( int64 A, char a, int64 B, char b, int64 C, char c, int64 D, char d, int64 E, char e) // 解の組み合わせを登録する
{
  char          buf[100];
  for( int mirror=0; mirror< 2; mirror++) {
    for( int rotate=0; rotate< 4; rotate++) {
      pack_goal( buf, A, a, B, b, C, c, D, d, E, e);
      goal.insert( buf);
      A = rotate90(A);
      B = rotate90(B);
      C = rotate90(C);
      D = rotate90(D);
      E = rotate90(E);
    }
    A = ::mirror(A);
    B = ::mirror(B);
    C = ::mirror(C);
    D = ::mirror(D);
    E = ::mirror(E);
  }
}

YesNo used[10];

void check( int64 orgC1, int64 orgC2, int64 orgC3, int64 C1, int64 C2, int64 C3, int64 A, int64 B) // 組み合わせて7x7の矩形になるか調べる
{
  // C1,C2,C3はC1が最大。C1を左上に置くことを前提にする。
  char          s[] = {'.','C','x','A','B'}; // 出力用
  char          S[] = {'C','C','C','A','B'}; // 重複チェック用
  int64         z[] = {C1,C2,C3,A,B};
  for( int a=0; a< 1; a++) {    // C1
    used[a] = YES;
    for( int b=1; b< 5; b++) {  // C2
      if( used[b]) continue;
      used[b] = YES;
      for( int c=1; c< 5; c++) { // C3
        if( used[c]) continue;
        used[c] = YES;
        for( int d=1; d< 5; d++) { // A
          if( used[d]) continue;
          used[d] = YES;
          for( int e=1; e< 5; e++) { // B
            if( used[e]) continue;
            used[e] = YES;
            int64         mat = 0; // 7x7の矩形
            int64   layoutA, layoutB, layoutC, layoutD, layoutE; // 実際のレイアウト
            if( layout(mat,z[a],layoutA) && // 順番に空いている場所に配置していく
                layout(mat,z[b],layoutB) && 
                layout(mat,z[c],layoutC) &&
                layout(mat,z[d],layoutD) && 
                layout(mat,z[e],layoutE) &&
                bitcount(mat) == 49) {// 全て配置できたら7x7の矩形を隙間が無いか調べる
              char  buf[128];
              pack_goal( buf, layoutA, S[a], layoutB, S[b], layoutC, S[c], layoutD, S[d], layoutE, S[e]);
              if( goal.find(buf) == goal.end()) { // 重複チェック
                regist_goal( layoutA, S[a], layoutB, S[b], layoutC, S[c], layoutD, S[d], layoutE, S[e]);
                num_goal++;                 // 初めて出現する状態なので
                ps( "find[%d]\n", num_goal); // 出力する
                ps( "6x6 Layout:\n");
                pack_goal( buf, orgC1, '.', orgC2, 'C', orgC3, 'x', 0, 0, 0, 0);
                ps( "%s", buf);
                pack_goal( buf, layoutA, s[a], layoutB, s[b], layoutC, s[c], layoutD, s[d], layoutE, s[e]);
                ps( "7x7 Layout:\n%s\n", buf);
              }
            }
            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();
  int64         A = bit0|bit1|bit2|bit7|bit8|bit9|bit14|bit15|bit16; // 3x3の矩形
  int64         B = bit0|bit1|bit7|bit8; // 2x2の矩形
  int64         orgC1, orgC2, orgC3; // 6x6を3分割した図形(6x6の分割を解と一緒に表示するために使用する)
  int64         C1, C2, C3;     // 6x6を3分割した図形,正規化したもの
  for( int row=1; row<= 5; row++) { // 横に切り込みを入れる位置
    for( int col=1; col<= 5; col++) { // 縦に切り込みを入れる位置
      cut66( row, col, C1, C2, C3, orgC1, orgC2, orgC3);  // 6x6の矩形を3分割する。最大図形をC1に返す
      // A,Bは正方形なので回転を考慮する必要は無いが,C1,C2,C3は回転を考慮する
      for( int r1=0; r1< 4; r1++) {
        for( int r2=0; r2< 4; r2++) {
          for( int r3=0; r3< 4; r3++) {
            check( orgC1, orgC2, orgC3, normalize(C1), normalize(C2), normalize(C3), A, B); // A,B,C1,C2,C3の5つの矩形で7x7になるか調べる
            C3  = rotate90(C3);
          }
          C2  = rotate90(C2);
        }
        C1  = rotate90(C1);
      }
    }
  }
  pt.end();
  ps( "%g秒\n", pt.sec());
  return    0;
}