#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#define NU_DEBUG using namespace std; #define REP(i,n) for(int i = 0; i < (int)(n); i++) #define FOR(i,c) for(__typeof((c).begin()) i = (c).begin(); i != (c).end(); ++i) #define ALLOF(c) (c).begin(), (c).end() typedef double decimal; const decimal EPS = 1e-8; const int MAXX = 4; const char EMPTY = '.'; #if 0 // for 4x4x4 typedef uint64_t ptn_t; #else typedef unsigned int ptn_t; #endif struct Piece { char id; int h, d, w; bool block[MAXX][MAXX][MAXX]; int nblocks; ptn_t mini; vector ptn; }; vector piece; int W, D, H; bool operator<(const Piece& a, const Piece& b){ return a.nblocks != b.nblocks ? a.nblocks < b.nblocks : a.mini < b.mini; } void dumppiece(Piece& p){ REP(h, p.h){ REP(d, p.d){ REP(w, p.w){ cerr << (p.block[h][d][w] ? p.id : EMPTY); } cerr << endl; } cerr << endl; } } void dumppattern(Piece& p){ REP(i, p.ptn.size()){ cerr << "Pattern #" << i << ":" << endl; ptn_t px = p.ptn[i]; REP(h, H){ REP(d, D){ REP(w, W){ bool yes = ((px >> (h * D * W + d * W + w)) & 1) == 1; cerr << (yes ? p.id : EMPTY); } cerr << endl; } cerr << endl; } } } #ifdef NU_DEBUG char field[MAXX][MAXX][MAXX]; void dumpfield(){ REP(h, H){ REP(d, D){ REP(w, W){ cerr << field[h][d][w]; } cerr << endl; } cerr << endl; } } void place(char id, ptn_t px){ int ix = 0; REP(h, H) REP(d, D) REP(w, W){ bool yes = ((px >> ix) & 1) == 1; if(yes) field[h][d][w] = id; ix++; } } void remove(char id, ptn_t px){ int ix = 0; REP(h, H) REP(d, D) REP(w, W){ bool yes = ((px >> ix) & 1) == 1; if(yes){ assert(field[h][d][w] == id); field[h][d][w] = EMPTY; } ix++; } } #endif int twos, ones; vector > g; vector matching; vector visited; bool augment(int left){ if(left < 0) return true; if(visited[left]) return false; visited[left] = true; REP(i, g[left].size()){ int right = g[left][i]; if(augment(matching[right])){ matching[right] = left; // cerr << left << " -> " << right << endl; return true; } } return false; } bool try_matching(ptn_t ptn){ // cerr << "matching " << ptn << endl; static bool p[MAXX][MAXX][MAXX]; int ix = 0; REP(h, H) REP(d, D) REP(w, W){ p[h][d][w] = ((ptn >> ix) & 1) == 0; ix++; } g.clear(); g.resize(H*D*W); ix = 0; REP(h, H) REP(d, D) REP(w, W){ if(!p[h][d][w]){ ix++; continue; } bool color = ((h+d+w)&1) == 0; if(color){ if(h > 0 && p[h-1][d][w]){ g[ix].push_back(ix - D*W); } if(h < H-1 && p[h+1][d][w]){ g[ix].push_back(ix + D*W); } if(d > 0 && p[h][d-1][w]){ g[ix].push_back(ix - W); } if(d < D-1 && p[h][d+1][w]){ g[ix].push_back(ix + W); } if(w > 0 && p[h][d][w-1]){ g[ix].push_back(ix - 1); } if(w < W-1 && p[h][d][w+1]){ g[ix].push_back(ix + 1); } } ix++; } #if 0 ix = 0; REP(h, H) REP(d, D) REP(w, W){ cerr << ix << ":"; REP(i, g[ix].size()){ cerr << " " << g[ix][i]; } cerr << endl; ix++; } #endif int matches = 0; matching.assign(H*D*W, -1); REP(ix, H*D*W){ if((int)g[ix].size() == 0) continue; visited.assign(H*D*W, false); if(augment(ix)){ matches++; } } // cerr << "match = " << matches << endl; return matches >= twos; } set checked; bool solve(int ix, ptn_t ptn){ if(ix < ones + twos){ #ifdef NU_DEBUG dumpfield(); #endif return try_matching(ptn); // return true; } #if 0 cerr << "ix = " << ix << ", ptn = "; REP(i, H*D*W){ bool yes = (ptn >> (H*D*W-1-i)) & 1; cerr << (yes ? 1 : 0); } cerr << endl; #endif if(piece[0].nblocks > 1){ ptn_t rest = ((1 << H*D*W) - 1) ^ ptn; ptn_t fill = (ptn_t)0; ptn_t prev = (ptn_t)0; REP(x, ix+1){ Piece& p = piece[x]; if(p.mini == prev) continue; prev = p.mini; REP(i, p.ptn.size()){ ptn_t px = p.ptn[i]; if(ptn & px) continue; fill |= px; } } if(fill != rest) return false; } Piece& p = piece[ix]; REP(i, p.ptn.size()){ ptn_t px = p.ptn[i]; if(ptn & px) continue; #ifdef NU_DEBUG place(p.id, px); #endif if(!checked.insert(ptn | px).second) continue; if(solve(ix-1, ptn | px)) return true; #ifdef NU_DEBUG // remove(p.id, px); #endif } return false; } void normalize(Piece& p){ int shift = 0; int delta; delta = 0; REP(h, p.h){ bool isempty = true; REP(d, p.d) REP(w, p.w){ if(p.block[h][d][w]){ isempty = false; shift = delta; } } if(isempty) delta++; } p.h -= delta; if(shift > 0) REP(h, p.h) REP(d, p.d) REP(w, p.w){ p.block[h][d][w] = p.block[h+shift][d][w]; } delta = 0; REP(d, p.d){ bool isempty = true; REP(h, p.h) REP(w, p.w){ if(p.block[h][d][w]){ isempty = false; shift = delta; } } if(isempty) delta++; } p.d -= delta; if(shift > 0) REP(h, p.h) REP(d, p.d) REP(w, p.w){ p.block[h][d][w] = p.block[h][d+shift][w]; } delta = 0; REP(w, p.w){ bool isempty = true; REP(h, p.h) REP(d, p.d){ if(p.block[h][d][w]){ isempty = false; shift = delta; } } if(isempty) delta++; } p.w -= delta; if(shift > 0) REP(h, p.h) REP(d, p.d) REP(w, p.w){ p.block[h][d][w] = p.block[h][d][w+shift]; } } void rotate_yoko(Piece& p){ bool buf[MAXX][MAXX]; REP(h, p.h){ REP(d, p.d) REP(w, p.w){ buf[d][w] = p.block[h][d][w]; } REP(d, p.w) REP(w, p.d){ p.block[h][d][w] = buf[w][p.w - 1 - d]; } } swap(p.d, p.w); } void rotate_tate(Piece& p){ bool buf[MAXX][MAXX]; REP(d, p.d){ REP(h, p.h) REP(w, p.w){ buf[h][w] = p.block[h][d][w]; } REP(h, p.w) REP(w, p.h){ p.block[h][d][w] = buf[w][p.w - 1 - h]; } } swap(p.h, p.w); } void generate(Piece& p){ p.ptn.clear(); REP(i, 12){ if(i & 3) rotate_yoko(p); else if(i > 0) rotate_tate(p); if(p.h > H || p.d > D || p.w > W) continue; REP(j, 2){ ptn_t ptn = 0; REP(h, p.h) REP(d, p.d) REP(w, p.w){ if(p.block[h][d][w]){ int ix[2]; ix[0] = h * D * W + d * W + w; ix[1] = (p.h-1-h) * D * W + (p.d-1-d) * W + w; ptn |= 1 << ix[j]; } } bool ok = true; REP(k, p.ptn.size()){ if(p.ptn[k] == ptn){ ok = false; break; } } if(!ok) continue; REP(h, H-p.h+1) REP(d, D-p.d+1) REP(w, W-p.w+1){ int shift = h * D * W + d * W + w; p.ptn.push_back(ptn << shift); } } } } int main(){ int nPieces; while(cin >> W >> D >> H >> nPieces){ if(W == 0 && D == 0 && H == 0 && nPieces == 0) break; bool res = true; piece.resize(nPieces); ones = twos = 0; REP(i, nPieces){ Piece& p = piece[i]; p.id = i < 26 ? i + 'A' : i < 52 ? i - 26 + 'a' : i < 62 ? i - 52 + '0' : i < 64 ? "+/"[i - 62] : '?'; int w, d, h; cin >> w >> d >> h; p.w = w; p.d = d; p.h = h; int blocks = 0; REP(hh, h){ REP(dd, d){ REP(ww, w){ char c; cin >> c; blocks += p.block[hh][dd][ww] = c == '*'; } } } p.nblocks = blocks; normalize(p); generate(p); if(p.ptn.size() == 0){ res = false; }else{ sort(ALLOF(p.ptn)); p.mini = p.ptn[0]; } if(p.nblocks == 1) ones++; if(p.nblocks == 2) twos++; } #if 0 REP(i, nPieces){ cerr << "Piece #" << i << ":" << endl; Piece& p = piece[i]; dumppiece(p); } #endif #if 0 REP(i, nPieces){ cerr << "Piece #" << i << ":" << endl; Piece& p = piece[i]; dumppattern(p); } #endif if(!res) goto shortcut; sort(ALLOF(piece)); #ifdef NU_DEBUG memset(field, EMPTY, sizeof(field)); #endif // piece[nPieces-1].ptn.resize(1); checked.clear(); res = solve(nPieces-1, (ptn_t)0); shortcut: cout << (res ? "Yes" : "No") << endl; } return 0; }