プログラムの出力結果は以下の通り。
Zorg: num point:62 : 0 1 2 3 4 5 6 7 8 9 10 11 12 0: . . . . . . 6 . . . . . . 1: . . . . . . 19 . . . . . . 2: . . 28 . 30 31 32 33 34 . 36 . . 3: 39 40 41 . 43 44 45 46 47 . 49 50 51 4: . . 54 55 56 57 58 59 60 61 62 . . 5: . . 67 68 69 70 71 72 73 74 75 . . 6: 78 79 80 81 82 83 84 85 86 . . . . 7: . . 93 94 95 96 97 98 99 100 101 . . 8: . . . 107 . . 110 . . 113 . . . 9: . . . 120 . . 123 . . 126 . . . 10: . . . . . . . . . . . . . 11: . . . . . . . . . . . . . 12: . . . . . . . . . . . . . Find:1 FIND_A: num point:31 : 0 1 2 3 4 5 6 7 8 9 10 11 12 0: . . . . . . 6 . . . . . . 1: . . . . . . 19 . . . . . . 2: . . 28 . 30 31 32 33 . . . . . 3: 39 40 41 . 43 44 . . . . . . . 4: . . 54 55 56 57 58 59 . . . . . 5: . . 67 68 69 . 71 . . . . . . 6: 78 79 80 . . . 84 . . . . . . 7: . . 93 94 95 . . . . . . . . 8: . . . 107 . . . . . . . . . 9: . . . 120 . . . . . . . . . 10: . . . . . . . . . . . . . 11: . . . . . . . . . . . . . 12: . . . . . . . . . . . . . FIND_B: num point:31 : 0 1 2 3 4 5 6 7 8 9 10 11 12 0: . . . . . . . . . . . . . 1: . . . . . . . . . . . . . 2: . . . . . . . . 34 . 36 . . 3: . . . . . . 45 46 47 . 49 50 51 4: . . . . . . . . 60 61 62 . . 5: . . . . . 70 . 72 73 74 75 . . 6: . . . 81 82 83 . 85 86 . . . . 7: . . . . . 96 97 98 99 100 101 . . 8: . . . . . . 110 . . 113 . . . 9: . . . . . . 123 . . 126 . . . 10: . . . . . . . . . . . . . 11: . . . . . . . . . . . . . 12: . . . . . . . . . . . . .
判りやすくすると以下の通り。
上記図形は以下のように同じ図形である。
プログラムのソースは以下の通り。結構長くなってしまった。
#include "puzutl.h" const int M = 13; // 図形全体の大きさ,プログラムを楽にするため縦横同じ大きさとする const byte BLK = 0xFF;// 図形内の空白 const int NUMX = 62; // 図形の矩形は全部で62個ある const int NUMA = 31; // 全部で62なので,2つの図形の半分は31個 const int NUMD = 7; // 最初に決まっているポイントは7点,7点に隣接する点も自動的に決まる const int NUMC = 20; // 最初の7点が決まったとき自動的に決まる点 cstring str_original_zukei[] = { // X :図形内で点のある場所 // A-G :図形を2つに分割したとき肝となる場所 // a-g :図形を2つに分割したとき大文字の点と同じ場所に分類される "......a......", "......a......", "..b.XXAXX.c..", "bbb.XXXXX.ccc", "..BXXXXXXXC..", "..XXXXXXXXX..", "ddDXXXXXX....", "..XEXXFXGgg..", "...e..f..g...", "...e..f..g...", ".............", // 全体の図形は横長なので縦を拡張 ".............", ".............", // ここが空でなくなる図形だとプログラムの修正が必要 }; struct Zukei { // M*Mの配列内で点がある場所 byte m_zu[M][M]; // 点のある場所はINDEX値,無い場所はBLK Zukei() { clear(); } void clear() { memset( m_zu, BLK, sizeof(byte)*M*M);} }; struct Point { // 図形内の位置情報 int m_row; int m_col; Point() { m_row = m_col = 0; } }; struct PointSub : public Point{ // 7点が決まったとき更に決まる点 int m_chr; // 7点の文字('A'=>0,'B'=>1,...) }; struct Trans { // 変換方法 int m_numRotate90; int m_numMirror; int m_row_diff; int m_col_diff; Trans( int numRotate90, int numMirror) { m_numRotate90 = numRotate90; m_numMirror = numMirror; m_row_diff = m_col_diff = 0; } Trans() { m_numRotate90 = m_numMirror = m_row_diff = m_col_diff = 0; } }; Zukei Zorg; PointSub point7Sub[NUMX]; // 7点が確定したとき,追加で確定する位置 int nPoint7Sub = 0; // 7点が確定したとき追加で確定する位置 set<string> findedSolution; // 同じ分割を2つ以上出力しないようにする inline YesNo exist( int row, int col) // 図形内の位置か? { if( row < 0 || row >= M) return NO; if( col < 0 || col >= M) return NO; return YES; } int rowcol_to_idx( int row, int col) { return row*M + col; } void rotate90( Zukei& zukeiNew, const Zukei& zukei) // 図形を左に90度回転する { byte val; for( int row=0; row< M; row++) { for( int col=0; col< M; col++) { zukeiNew.m_zu[M-col-1][row] = zukei.m_zu[row][col]; } } } void mirror( Zukei& zukeiNew, const Zukei& zukei) // 図形を左右対象にする { byte c; for( int row=0; row< M; row++) { for( int col=0; col<= M/2; col++) { zukeiNew.m_zu[row][col] = zukei.m_zu[row][M-col-1]; zukeiNew.m_zu[row][M-col-1] = zukei.m_zu[row][col]; } } } int init_zukei( Zukei& zukei, Point* point) // 文字列で与えられた図形をプログラムで使う形式に変換する。 { int cnt = 0; // 図形内の点の数 int npoint = 0; for( int row=0; row< M; row++) { for( int col=0; col< M; col++) { char c = str_original_zukei[row][col]; zukei.m_zu[row][col] = BLK; // 点が無い if( c != '.') zukei.m_zu[row][col] = cnt; // 点がある if( c >= 'A' && c <= 'G') { assert( npoint<NUMD); point[npoint].m_row = row; point[npoint].m_col = col; npoint++; } if( c >= 'a' && c <= 'g') { assert( nPoint7Sub < NUMC); point7Sub[nPoint7Sub].m_row = row; point7Sub[nPoint7Sub].m_col = col; point7Sub[nPoint7Sub].m_chr = c-'a'; nPoint7Sub++; } cnt++; } } assert( npoint == NUMD); assert( nPoint7Sub == NUMC); return npoint; } void assign_point_to_zukei( Zukei& A, Point Ap[], int& numAp, Zukei& B, Point Bp[], int& numBp, Point point[NUMD], int bit) { // 最初の7点をA,Bの図形に振り分ける。 // Ap[], Bp[] は最初の7点に付随する点で正規化する前の図形の点の位置 A.clear(); B.clear(); numAp = numBp = 0; int ab[NUMD]; for( int i=0; i< NUMD; i++) { int row = point[i].m_row, col = point[i].m_col; if( bit&1) { B.m_zu[row][col] = rowcol_to_idx( row, col); Bp[numBp].m_row = row; Bp[numBp].m_col = col; numBp++; ab[i] = 2; // B } else { A.m_zu[row][col] = rowcol_to_idx( row, col); Ap[numAp].m_row = row; Ap[numAp].m_col = col; numAp++; ab[i] = 1; // A } bit >>= 1; } // 振り分けが終わったら,7点以外に振り分けできる点を振り分ける。 for( i=0; i< nPoint7Sub; i++) { int row = point7Sub[i].m_row, col = point7Sub[i].m_col; if( ab[point7Sub[i].m_chr] == 1) { // Aに分類 A.m_zu[row][col] = rowcol_to_idx(row,col); Ap[numAp].m_row = row; Ap[numAp].m_col = col; numAp++; } else { // Bに分類 B.m_zu[row][col] = rowcol_to_idx(row,col); Bp[numBp].m_row = row; Bp[numBp].m_col = col; numBp++; } } } void find_non_blank_row_col_idx( const Zukei& z, int& row, int& col) { for( row=0; row< M; row++) { for( col=0; col< M; col++) { if( z.m_zu[row][col] != BLK) { // colはこれより小さな位置にかこれと等しい位置にあるはず。 for( int c=0; c< col; c++) { for( int r=0; r< M; r++) { if( z.m_zu[r][c] != BLK) { col = c; return; } } } col = c; return; } } } assert(0); // 図形が空の場合は何らかのエラーがあったと予想される } void normalize( Zukei& z, Trans& trans) { // 図形を左上に配置する int row, col; find_non_blank_row_col_idx( z, row, col); trans.m_row_diff = row; trans.m_col_diff = col; memcpy( z.m_zu, (byte*)z.m_zu+row*sizeof(byte)*M, (M-row)*sizeof(byte)*M); memcpy( z.m_zu, (byte*)z.m_zu+col*sizeof(byte) , (M*M-col)*sizeof(byte)); // 下のrow行に空白を設定する。 memset( (byte*)z.m_zu+(M-row)*sizeof(byte)*M, BLK, (row)*sizeof(byte)*M); // 右のcol列には自動で空白が入っているはずなので何もしなくて良い。 } void doTrans( const Trans& T, int row, int col, int& newRow, int& newCol) { newRow = row; newCol = col; if( T.m_numMirror) { newCol = M - newCol - 1; } int tmpRow, tmpCol; for( int r=0; r< T.m_numRotate90; r++) { tmpRow = newRow, tmpCol = newCol; newRow = tmpCol; newCol = M-tmpRow-1; } newRow -= T.m_row_diff; newCol -= T.m_col_diff; } void reverseTrans( const Trans& T, int row, int col, int& newRow, int& newCol) { newRow = row; newCol = col; newRow += T.m_row_diff; newCol += T.m_col_diff; int tmpRow, tmpCol; for( int r=0; r< T.m_numRotate90; r++) { tmpRow = newRow, tmpCol = newCol; newRow = M-tmpCol-1; newCol = tmpRow; } if( T.m_numMirror) { newCol = M - newCol - 1; } } YesNo match( const Zukei& A, const Trans& transA, Point Ap[], int numApFrom, int numApTo, const Trans& transB, Zukei& mergeA, Zukei& mergeB, int mergeType) { int rowAdd, colAdd, rowA, colA, rowB, colB; for( int idx=numApFrom; idx< numApTo; idx++) { // Aに追加された点に対応する点がB上に存在するか? rowAdd = Ap[idx].m_row; // 追加された点 colAdd = Ap[idx].m_col; if( mergeType == 1) mergeA.m_zu[rowAdd][colAdd] = rowcol_to_idx( rowAdd, colAdd); if( mergeType == 2) mergeB.m_zu[rowAdd][colAdd] = rowcol_to_idx( rowAdd, colAdd); // これがAの正規化された図形のどこにMAPされるか? doTrans( transA, rowAdd, colAdd, rowA, colA); // これがBの元の図形のどこに相当するか? reverseTrans( transB, rowA, colA, rowB, colB); // この位置が元の図形に存在するか? if( !exist(rowB,colB)) return NO; assert( rowB >= 0 && rowB < M); assert( colB >= 0 && colB < M); if( Zorg.m_zu[rowB][colB] == BLK) { return NO; // 元の図形に存在しない点である } if( mergeType == 1) mergeB.m_zu[rowB][colB] = rowcol_to_idx( rowB, colB); if( mergeType == 2) mergeA.m_zu[rowB][colB] = rowcol_to_idx( rowB, colB); } return YES; } void mergeZukei( Zukei& z, const Zukei& A, const Zukei& B) { for( int row=0; row< M; row++) { for( int col=0; col< M; col++) { z.m_zu[row][col] = BLK; assert( A.m_zu[row][col] == BLK || B.m_zu[row][col] == BLK); // 2つの図形を重ねるとき両方の図形に同じ点が使われているのは拙い if( A.m_zu[row][col] != BLK) z.m_zu[row][col] = A.m_zu[row][col]; if( B.m_zu[row][col] != BLK) z.m_zu[row][col] = B.m_zu[row][col]; } } } YesNo match( const Zukei& A, const Trans& transA, Point Ap[], int numApFrom, int numApTo, const Zukei& B, const Trans& transB, Point Bp[], int numBpFrom, int numBpTo, Zukei& mergeA, Point mergeAp[], int& numMergeAp, Zukei& mergeB) { mergeA.clear(); mergeB.clear(); Zukei x; // 2つの図形が重なるかどうか調べる。図形全体を調べるのは手間がかかるので,調べるべき点だけを調べる。 // aに追加された図形の点がbにあるかどうか調べる。bに無くても問題ないが,bで既に使われていたら駄目。 // Bに追加された点がAに… if( match( A, transA, Ap, numApFrom, numApTo, transB, mergeA, mergeB, 1) && match( B, transB, Bp, numBpFrom, numBpTo, transA, mergeA, mergeB, 2)) { numMergeAp = 0; for( int row=0; row< M; row++) { for( int col=0; col< M; col++) { if( mergeA.m_zu[row][col] != BLK) { assert(numMergeAp<NUMX); mergeAp[numMergeAp].m_row = row; mergeAp[numMergeAp].m_col = col; numMergeAp++; } } } return YES; } return NO; } void exist_and_nouse( const Zukei& A, const Zukei& B, Zukei& z, int row, int col, Point newPoint[], int& numNewPoint) { if( !exist(row,col)) return; if( Zorg.m_zu[row][col] == BLK) return; if( A.m_zu[row][col] != BLK) return; // 既に使われている if( B.m_zu[row][col] != BLK) return; if( z.m_zu[row][col] != BLK) return; newPoint[numNewPoint].m_row = row; newPoint[numNewPoint].m_col = col; numNewPoint++; assert( row != 0 && col != 0); z.m_zu[row][col] = rowcol_to_idx(row,col); } int find_point_nouse( const Zukei& A, const Zukei& B, Point Ap[], int low, int numAp, Point newPoint[]) { Zukei z; int row, col; int numNewPoint = 0; for( int i=low; i< numAp; i++) { // この点の周りでまだ使われていない点を探す。 // 上,下,左,右を調べる。 row = Ap[i].m_row; col = Ap[i].m_col; assert( (row != 0) || (col != 0)); exist_and_nouse( A, B, z, row-1, col+0, newPoint, numNewPoint); exist_and_nouse( A, B, z, row+1, col+0, newPoint, numNewPoint); exist_and_nouse( A, B, z, row+0, col-1, newPoint, numNewPoint); exist_and_nouse( A, B, z, row+0, col+1, newPoint, numNewPoint); } return numNewPoint; } string zukei_to_str( const Zukei& zukei) // 同じ図形が解として見つかっても複数出力しないように判断するために利用する { char buf[M*M*4+1]; astring p = buf; for( int row=0; row< M; row++) { for( int col=0; col< M; col++) { sprintf( p, "%3d.", zukei.m_zu[row][col]); // どんな文字列になっても図形が違えば違う文字列になれば良い p += 4; } } return buf; } void dump( const Zukei& z, cstring msg) { int row, col; int cnt = 0; for( row=0; row< M; row++) { for( int col=0; col< M; col++) { if( z.m_zu[row][col] != BLK) cnt++; } } ps( "%s: num point:%d\n", msg, cnt); ps( " : ");for( col=0; col< M; col++) ps( "%3d ", col);ps("\n"); for( row=0; row< M; row++) { ps( "%2d: ", row); for( int col=0; col< M; col++) { if( z.m_zu[row][col] == BLK) ps( " . "); else ps( "%3d ", z.m_zu[row][col]); } ps( "\n"); } ps( "\n"); } void is_zukei_delete_countinous( Zukei& zukei, int row, int col) { if( zukei.m_zu[row][col] != BLK) zukei.m_zu[row][col] = BLK; if( exist( row-1, col+0) && zukei.m_zu[row-1][col+0] != BLK) is_zukei_delete_countinous( zukei, row-1, col+0); if( exist( row+1, col+0) && zukei.m_zu[row+1][col+0] != BLK) is_zukei_delete_countinous( zukei, row+1, col+0); if( exist( row+0, col-1) && zukei.m_zu[row+0][col-1] != BLK) is_zukei_delete_countinous( zukei, row+0, col-1); if( exist( row+0, col+1) && zukei.m_zu[row+0][col+1] != BLK) is_zukei_delete_countinous( zukei, row+0, col+1); } YesNo is_zukei_continuous( const Zukei& zukei) { Zukei zukei_check = zukei; int row, col; int cnt = 0; for( row=0; row< M; row++) { for( int col=0; col< M; col++) { if( zukei_check.m_zu[row][col] != BLK) { if( cnt++ == 0) is_zukei_delete_countinous( zukei_check, row, col); } } } return (cnt==1)?YES:NO; } void findNext( Zukei& A, Zukei& B, Point Ap[], int numAp, const Trans& transA, const Trans& transB) { Point newPoint[NUMX]; // 図形Aの周りの点で,図形Aに追加できる点を探して設定する int numNewPoint = 0; int row, col; if( numAp >= NUMA) { // 全部の点のうち半分を決定できたので解が見つかった string str_zukei = zukei_to_str(A); is_zukei_continuous(A); is_zukei_continuous(B) ; if( findedSolution.find( str_zukei) == findedSolution.end()) { // 同じ解を出力しないように既に出現した解を記録しておく。 ps( "Find:%d\n", findedSolution.size()+1); dump(A,"FIND_A"); dump(B,"FIND_B"); findedSolution.insert( str_zukei); } return; } // まず新しく追加した点の周りにで拡張できる点を探す。見つからないときは既に全ての点から探す。 numNewPoint = find_point_nouse( A, B, Ap, numAp-1, numAp, newPoint); // 厳密に言うとこの部分は問題あり。main()から呼ばれるとき0にならないと他を探さない if( numNewPoint == 0) numNewPoint = find_point_nouse( A, B, Ap, 0, numAp, newPoint); int rowAdd, colAdd, rowA, colA, rowB, colB; for( int i=0; i< numNewPoint; i++) { rowAdd = newPoint[i].m_row; // 新しく図形Aに追加しようとしている点 colAdd = newPoint[i].m_col; doTrans( transA, rowAdd, colAdd, rowA, colA); // その点がAを正規化した図形のどこに配置されるか? assert( exist(rowA,colA)); // Aの場合必ず図形内に配置されるはず reverseTrans( transB, rowA, colA, rowB, colB); // それが正規化する前の図形Bのどこに配置されるか? if( !exist(rowB,colB)) continue; // その位置が図形からはみ出してしまえばこの点はAに追加できない if( Zorg.m_zu[rowB][colB] == BLK) continue; // 元の図形に存在しない位置であるのでこの点はAに追加できない if( A.m_zu[rowB][colB] != BLK) continue; // この点は既にAの図形が使っている if( B.m_zu[rowB][colB] != BLK) continue; // この点は既にBの図形が使っている assert( A.m_zu[rowAdd][colAdd] == BLK); assert( B.m_zu[rowB][colB] == BLK); if( B.m_zu[rowB][colB] != BLK) ps( "B*[%d][%d]=%d\n", rowB, colB, B.m_zu[rowB][colB]); A.m_zu[rowAdd][colAdd] = rowcol_to_idx(rowAdd,colAdd); B.m_zu[rowB][colB] = rowcol_to_idx( rowB, colB); Ap[numAp].m_row = rowAdd; Ap[numAp].m_col = colAdd; findNext( A, B, Ap, numAp+1, transA, transB); B.m_zu[rowB][colB] = BLK; A.m_zu[rowAdd][colAdd] = BLK; } } int main( int argc, cstring argv[]) { Zukei A, B, mergeA, mergeB; Zukei A1, B1, B2, B3, B4, C1, C2, C3, C4; Trans baseTransA1(0,0), baseTransB1(0,0), baseTransB2(1,0), baseTransB3(2,0), baseTransB4(3,0), baseTransC1(0,1), baseTransC2(1,1), baseTransC3(2,1), baseTransC4(3,1); // 正規化を記録 Point point7[NUMD]; // この7点の位置 Point Ap[NUMX], Bp[NUMX], mergeAp[NUMX]; // A,Bの図形上の点 int numAp, numBp, numMergeAp; // 最初に決まっている7点と,矩形の位置を求める。 int n = init_zukei( Zorg, point7); dump(Zorg,"Zorg"); ProcTime pt;pt.start(); // 7点から1点,2点,3点を選択し解を求める。 // 3点まででOK。4点になると,3点の場合と逆になるだけなので。 // 選んだ方の図形をBとする。 for( int selnum=1; selnum< 4; selnum++) { // 選択する点を7ビットから選ぶものとする。 for( int bit=1; bit<= 0x7F; bit++) { if( bitcount(bit) == selnum) { // ビットがONの場所を図形Bに選択して検索する。 // 図形にポイントを設定する。 assign_point_to_zukei( A, Ap, numAp, B, Bp, numBp, point7, bit); Trans transA1=baseTransA1; Trans transB1=baseTransB1,transB2=baseTransB2,transB3=baseTransB3,transB4=baseTransB4; Trans transC1=baseTransC1,transC2=baseTransC2,transC3=baseTransC3,transC4=baseTransC4; // 図形の可能性を調べる。 A1 = A; B1 = B; rotate90(B2,B1); rotate90(B3,B2); rotate90(B4,B3); mirror(C1,B1); mirror(C2,B2); mirror(C3,B3); mirror(C4,B4); // 比較用に図形を正規化する normalize(A1,transA1); normalize(B1,transB1); normalize(B2,transB2); normalize(B3,transB3); normalize(B4,transB4); normalize(C1,transC1); normalize(C2,transC2); normalize(C3,transC3); normalize(C4,transC4); // 図形が重なる可能性があるか調べ,可能性があれば更に可能性を追求する。 if( match( A1, transA1, Ap, 0, numAp, B1, transB2, Bp, 0, numBp, mergeA, mergeAp, numMergeAp, mergeB)) findNext( mergeA, mergeB, mergeAp, numMergeAp, transA1, transB1); if( match( A1, transA1, Ap, 0, numAp, B2, transB2, Bp, 0, numBp, mergeA, mergeAp, numMergeAp, mergeB)) findNext( mergeA, mergeB, mergeAp, numMergeAp, transA1, transB2); if( match( A1, transA1, Ap, 0, numAp, B3, transB2, Bp, 0, numBp, mergeA, mergeAp, numMergeAp, mergeB)) findNext( mergeA, mergeB, mergeAp, numMergeAp, transA1, transB3); if( match( A1, transA1, Ap, 0, numAp, B4, transB2, Bp, 0, numBp, mergeA, mergeAp, numMergeAp, mergeB)) findNext( mergeA, mergeB, mergeAp, numMergeAp, transA1, transB4); if( match( A1, transA1, Ap, 0, numAp, C1, transC2, Bp, 0, numBp, mergeA, mergeAp, numMergeAp, mergeB)) findNext( mergeA, mergeB, mergeAp, numMergeAp, transA1, transC1); if( match( A1, transA1, Ap, 0, numAp, C2, transC2, Bp, 0, numBp, mergeA, mergeAp, numMergeAp, mergeB)) findNext( mergeA, mergeB, mergeAp, numMergeAp, transA1, transC2); if( match( A1, transA1, Ap, 0, numAp, C3, transC2, Bp, 0, numBp, mergeA, mergeAp, numMergeAp, mergeB)) findNext( mergeA, mergeB, mergeAp, numMergeAp, transA1, transC3); if( match( A1, transA1, Ap, 0, numAp, C4, transC2, Bp, 0, numBp, mergeA, mergeAp, numMergeAp, mergeB)) findNext( mergeA, mergeB, mergeAp, numMergeAp, transA1, transC4); } } } pt.end(); pe( "%g 秒\n", pt.sec()); return 0; }