// Solution to "Efil" by Bob Roos // // This solution attempts to be "clever" to reduce the computation time, // but it is still dog-slow for 4-by-5 grids. The basic idea is backtracking, // but instead of filling in the parent candidate cell-by-cell, it is filled // in with overlapping 3-by-3 configurations chosen from one of two lists, // depending on whether we are trying to create a "live" cell or a "dead" // cell from that 3-by-3 configuration. Thus, we start out by filling nine // cells with a pattern, then we find a consistent overlapping pattern // and fill six more cells, etc. But I think I need to do more preprocessing. // For instance, I need a way to quickly look up all 9-bit patterns that // contain a certain subpattern rather than doing a tedious "compatibility // test" for each pattern added. // // Note to anyone reading this--I made a lot of false starts, so there is // almost certainly some leftover useless code in here! import java.io.*; import java.util.*; public class Efil { public static BufferedReader in; public static StringTokenizer tok; public static int m,n,k; public static int casenum; public static boolean goal[][]; public static boolean used[][]; public static int anc[][]; public static int ancCount[][]; public static String line; public static boolean alive[] = new boolean[512]; public static int nbrCount[] = new int[512]; public static int middle = 1<<4; // 000/010/000 public static Vector nextlive; public static Vector nextdead; public static Hashtable nextStatus = new Hashtable(); public static int numFilled; public static int numParents; public static void main(String[] args) throws IOException { in = new BufferedReader(new InputStreamReader(System.in)); line = in.readLine().trim(); tok = new StringTokenizer(line); m = Integer.parseInt(tok.nextToken()); n = Integer.parseInt(tok.nextToken()); casenum = 0; // Preprocessing -- store as much information as possible about // the 3x3 configurations. // // Each configuration is represented by the integer corresponding // to the 9-bit pattern represented by the 3x3 block of cells, // in row-major order. For each configuration, remember whether its // central cell is alive or dead, and how many live neighbors the // central cell has: nextlive = new Vector(); // nextlive and nextdead partition configs. by nextdead = new Vector(); // whether center cell is alive/dead in next gen. for (int i = 0; i < 512; i++) { // there are 2^9 3x3 configurations alive[i] = (i & middle) != 0; // is central cell alive? nbrCount[i] = 0; int nbrs = i & (~middle); for (int j = 0; j < 9; j++) if (((nbrs >> j) & 1) != 0) nbrCount[i]++; if (alive[i]) { if (nbrCount[i] >= 2 && nbrCount[i] <= 3) { nextlive.add(new Integer(i)); nextStatus.put(new Integer(i),new Boolean(true)); } else { nextdead.add(new Integer(i)); nextStatus.put(new Integer(i),new Boolean(false)); } } else { if (nbrCount[i] == 3) { nextlive.add(new Integer(i)); nextStatus.put(new Integer(i),new Boolean(true)); } else { nextdead.add(new Integer(i)); nextStatus.put(new Integer(i),new Boolean(false)); } } } // Main input/process/output loop: while (m >0 && n > 0) { casenum++; goal = new boolean[m][n]; // input configuration used = new boolean[m][n]; // is this cell accounted for in the parent? anc = new int[m][n]; ancCount = new int[m][n]; // how many "active" goal cells depend on this? for (int i = 0; i < m; i++) for (int j = 0; j < n; j++) { goal[i][j] = false; used[i][j] = false; ancCount[i][j] = 0; } // Read in the positions of the occupied cells: k = Integer.parseInt(in.readLine().trim()); for (int i = 0; i < k; i++) { line = in.readLine().trim(); tok = new StringTokenizer(line); int r = Integer.parseInt(tok.nextToken()); int c = Integer.parseInt(tok.nextToken()); goal[r][c] = true; } //debug(goal); process(); // Get set up for next case: line = in.readLine().trim(); tok = new StringTokenizer(line); m = Integer.parseInt(tok.nextToken()); n = Integer.parseInt(tok.nextToken()); } } // For debugging only: prints out the grid: public static void debug(boolean grid[][]) { System.out.print("+"); for (int j = 0; j < n; j++) System.out.print("---+"); System.out.println(); for (int i = 0; i < m; i++) { System.out.print("|"); for (int j = 0; j < n; j++) if (grid[i][j]) System.out.print(" O |"); else System.out.print(" |"); System.out.println(); System.out.print("+"); for (int j = 0; j < n; j++) System.out.print("---+"); System.out.println(); } } // Mod function adjusted to always give nonnegative result: public static int mod(int a, int b) { int temp = a % b; if (temp < 0) temp += b; return temp; } // See if the 3x3 ancestor candidate pattern is compatible with the // 3x3 portion of the ancestor grid centered at (r,c). public static boolean compatible(int pattern, int r, int c) { // Special cases: if (m == 1) { if ((pattern & 7) != ((pattern >> 3) & 7)) return false; if ((pattern & 7) != ((pattern >> 6) & 7)) return false; if (((pattern >> 3) & 7) != ((pattern >> 6) & 7)) return false; } if (m == 2) { if ((pattern & 7) != ((pattern >> 6) & 7)) return false; } if (n == 1) { if ((pattern & 73) != ((pattern >> 1) & 73)) return false; if ((pattern & 73) != ((pattern >> 2) & 73)) return false; if (((pattern >> 1) & 73) != ((pattern >> 2) & 73)) return false; } if (n == 2) { if ((pattern & 73) != ((pattern >> 2) & 73)) return false; } int temp = 0; int pos = 8; int mask = 0; for (int i = r-1; i <= r+1; i++) for (int j = c-1; j <= c+1; j++) { int i1 = mod(i,m); int j1 = mod(j,n); if (used[i1][j1]) { // if this cell in the ancestor grid is used, ... temp += anc[i1][j1]<> pos); ancCount[i1][j1] = 1; numFilled++; } pos--; } } } // "Unfill" the 3x3 portion of the ancestor grid centered at r,c with // the 3x3 pattern; assume that the pattern is compatible, i.e., // assume previous call to "compatible(pattern,r,c)": public static void unfill(int pattern, int r, int c) { int pos = 8; for (int i = r-1; i <= r+1; i++) { for (int j = c-1; j <= c+1; j++) { int i1 = mod(i,m); int j1 = mod(j,n); if (used[i1][j1]){ ancCount[i1][j1]--; if (ancCount[i1][j1] <= 0) { used[i1][j1] = false; numFilled--; } } else { System.out.println("Error in unfill -- unable to undo from [" + i1 + "]["+j1+"]"); } pos--; } } } public static void process() { numFilled = 0; numParents = 0; // start recursive backtracking to fill in grid: recursiveFill(0,0); if (numParents > 0) System.out.println("Case " + casenum +": "+ numParents + " possible ancestors."); else System.out.println("Case " + casenum + ": Garden of Eden."); } public static void recursiveFill(int r, int c) { Vector choices; if (goal[r][c]) choices = nextlive; else choices = nextdead; for (int i = 0; i < choices.size(); i++) { int pattern = ((Integer)(choices.get(i))).intValue(); if (compatible(pattern,r,c)) { fill(pattern,r,c); if (numFilled == m*n) { if (verify()) numParents++; } else { boolean found = false; int nextRow = r; int nextCol = c+1; if (nextCol >= n) { nextRow++; nextCol = 0; } boolean skip = false; while (!found) { if (!used[nextRow][nextCol]) { break; } // LAST DITCH ATTEMPT FOR AN "EASY" SPEED UP (NOT MUCH USE): // this cell is used, so see if we can prune the search: boolean check = true; for (int p = nextRow-1; p <= nextRow+1; p++) for (int q = nextCol-1; q <= nextCol+1; q++) { int p1 = mod(p,m); int q1 = mod(q,n); if (!used[p1][q1]) check = false; } if (check && !((Boolean)(nextStatus.get(new Integer(bitpattern(nextRow,nextCol)))) .equals(new Boolean(goal[nextRow][nextCol])))) { skip = true; break; } nextCol++; if (nextCol >= n) { nextRow++; nextCol = 0; } } if (!skip) recursiveFill(nextRow, nextCol); } unfill(pattern,r,c); } } } public static int bitpattern(int r, int c) { int pos = 8; int temp = 0; for (int i = r-1; i <= r+1; i++) for (int j = c-1; j <= c+1; j++) { int i1 = mod(i,m); int j1 = mod(j,n); temp += anc[i1][j1]<