朝日新聞2006年5月12日のパズル横丁問題

問題

赤,青,黄,緑の1x2の大きさのブロックが,それぞれ3個ずつ合計12個ある。

これを5x5の矩形に重ならないように配置する。縦,横どの列を見ても同じ色のブロックが2個以上無いように配置するにはどうすれば良いか?

解答への道(ヒント)

ブロックを縦置き,横置きの2通り考えると5x5の矩形に約25x2=50通り(実際は40通り)の置き方がある。

同じ色のブロックが3個あるから,それを配置する場合の数は約40x40x40(64,000)通り。

虱潰すことを考えると4色あるので,64,0004(16777216000000000000)通り。

実用的でないので,仕様に沿った制約を組む。

同じ色のブロックを矩形に配置するとき,40x40x40でなく,同じ色のブロックが同一列上に2個以上無いようにする。

例えば上図で×の配置は出来ない。

この絞り込み部分のプログラムは以下のようになる。

  // まず,5x5の矩形にビットを割り当てる。
  int bits[5][5];
  for( row=0; row< 5; row++) {
    for( col=0; col< 5; col++) {
      bits[row][col] = bit;
      bit<<=1;
    }
  }
  // ブロックをビットで表現する。
  // ブロックの置き方は,25カ所に縦,横の2通りの置き方があるので,高々25x2通り=50通り。
  int color[50];                // 高々50通りの置き方がある
  int ncolor = 0;
  for( row=0; row< 5; row++) {
    for( col=0; col< 5; col++) {
      if( exist(row+0,col+1)) { // 横置き
        color[ncolor++] = bits[row][col] | bits[row+0][col+1];
      }
      if( exist(row+1,col+0)) { // 縦置き
        color[ncolor++] = bits[row][col] | bits[row+1][col+0];
      }
    }
  }
  // 全部でncolor通りの置き方があった。
  // 今度は,ncolor通りの置き方を3つ組み合わせた置き方がいくつあるか調べる。
  // 同じ色のブロックが3個あるので。最大ncolor*ncolor*ncolor通りの置き方がある。
  int* pattern = new int[ncolor*ncolor*ncolor];
  int npattern = 0;             // 実際に何通りの置き方があるか。
  for( i=0; i< ncolor; i++) {
    for( j=i+1; j< ncolor; j++) {
      for( k=j+1; k< ncolor; k++) {
        if( (color[i] & color[j]) ||
            (color[j] & color[k]) ||
            (color[i] & color[k])) {
          // ブロック同士が重なっている。同じ位置に置けない
          continue;
        }
        // 同じ行,列に置けない。
        if( samerc( color[i], color[j], color[k])) continue;
        // 5x5の矩形に同じ色の3個のブロックを置けた。
        pattern[npattern] = color[i] | color[j] | color[k];
        npattern++;
      }
    }
  }

実際に求めてみるとnpatternが144になる。ここから先は虱潰しても大したことは無い。

  // ブロック3個を5x5に置く場合の数は全部でnpatternある。
  int colorA, colorAB, colorABC, colorABCD;
  int a,b,c,d;
  // この組み合わせで互いに重ならないように置けるか調べる。
  for( a=0; a< npattern; a++) {
    colorA      = pattern[a];
    for( b=a+1; b< npattern; b++) {
      if( colorA & pattern[b]) continue;
      colorAB    = colorA | pattern[b];
      for( c=b+1; c< npattern; c++) {
        if( colorAB & pattern[c]) continue;
        colorABC    = colorAB | pattern[c];
        for( d=c+1; d< npattern; d++) {
          if( colorABC & pattern[d]) continue;
          colorABCD    = colorABC | pattern[d];
          // Find
        }
      }
    }
  }

結果,2件が出力されました。向きが違うだけなので実質1件。

 

木曜夜中にフジテレビで「コマネチ大学数学科」という番組をやっている。結構面白いと思う。

解速度