// 2007 ACM Mid-Central USA Regional Programming Contest // Solution to Problem C: "The Seven Percent Slink" [hard] // Ron Pachecho import java.io.*; import java.util.Scanner; import java.util.*; class slink { public static void main( String[] args ) throws Exception { String inFile = args.length > 0 ? args[0] : "slink.in"; Scanner in = new Scanner( new File( inFile ) ); int n = 1; int r = in.nextInt(); while ( r > 0 ) { System.out.println( n++ ); SlinkPuzzle s = new SlinkPuzzle( in, r, in.nextInt() ); s.solve(); s.print(); r = in.nextInt(); } } private static class SlinkPuzzle { private int _rows, _cols; private char[][] _board; private boolean _isBoardModified; public final static char ANY = '\000', BLOCKED = 'X', EMPTY = '.', LINKED = '+'; private class Position { protected int __row, __col; public Position() { __row = 0; __col = 0; } public Position( Position pos ) { __row = pos.__row; __col = pos.__col; } public Position( int row, int col ) { __row = row; __col = col; } public void goTo( int row, int col ) { __row = row; __col = col; } public Position north() { return new Position( __row - 1, __col ); } public Position south() { return new Position( __row + 1, __col ); } public Position west() { return new Position( __row, __col - 1 ); } public Position east() { return new Position( __row, __col + 1 ); } public boolean isValid() { return __row >= 0 && __col >= 0 && __row < _rows && __col < _cols; } public char getState() { return ( isValid() ? _board[__row][__col] : BLOCKED ); } public void setState( char state ) { if ( isValid() ) { if ( _board[__row][__col] != state ) _isBoardModified = true; if ( state == LINKED && isHzEdge() ) { west().setState( state ); east().setState( state ); } else if ( state == LINKED && isVtEdge() ) { north().setState( state ); south().setState( state ); } _board[__row][__col] = state; } } public void setState( int state ) { setState( Character.forDigit( state, 10 ) ); } public boolean isState( char state ) { return state == ANY || getState() == state; } public boolean isHzEdge() { return __row % 2 == 0 && __col % 2 == 1; } public boolean isVtEdge() { return __row % 2 == 1 && __col % 2 == 0; } public boolean isVertex() { return __row % 2 == 0 && __col % 2 == 0; } public boolean isCell() { return __row % 2 == 1 && __col % 2 == 1; } } private class PositionIterator extends Position { private int __startRow, __startCol, __rows, __cols; public PositionIterator() { __startRow = 0; __startCol = 0; __rows = _rows; __cols = _cols; reset(); } public PositionIterator( int row, int col, int rows, int cols ) { __startRow = row; __startCol = col; __rows = rows; __cols = cols; reset(); } public PositionIterator next() { __col += 1; __row += ( __col - __startCol ) / __cols; __col = ( __col - __startCol ) % __cols + __startCol; return this; } public void reset() { goTo( __startRow, __startCol -1 ); } public boolean isLastCol() { return __col == __startCol + __cols - 1; } } private class CellIterator extends Position { public CellIterator() { super( 1, -1 ); } public CellIterator next() { __col += 2; __row += 2 * ( __col / _cols ); __col = __col % ( _cols - 1 ); return this; } public void reset() { goTo( 1, -1 ); } } public SlinkPuzzle( Scanner in, int puzzleRows, int puzzleCols ) { _rows = 2 * puzzleRows + 1; _cols = 2 * puzzleCols + 1; _board = new char[_rows][_cols]; PositionIterator pos = new PositionIterator(); while ( pos.next().isValid() ) pos.setState( EMPTY ); CellIterator cell = new CellIterator(); while ( cell.next().isValid() ) cell.setState( in.nextInt() ); } public void solve() { rules1(); do { do { _isBoardModified = false; rules2(); } while ( _isBoardModified ); } while ( _isBoardModified ); } // rules applied only once private void rules1() { // block all edges around zeros rule( 3, 3, 0, 0, 0, false, "...|.0.|...", ".X.|X.X|.X." ); // adjacent threes rule( 5, 5, 1, 0, 1, false, ".....|.....|.3.3.|.....|.....", "..X..|.....|+.+.+|.....|..X.." ); // diagonal threes rule( 5, 5, 0, 0, 1, false, ".....|.3...|.....|...3.|.....", "++...|+....|.....|....+|...++" ); } // rules applied repeatedly private void rules2() { // three blocked on one side rule( 3, 3, 0, 0, 3, false, "...|X3.|...", "+++|..+|+++" ); // two blocked on two sides rule( 3, 3, 0, 0, 3, false, ".X.|X2.|...", "..+|..+|+++" ); rule( 3, 3, 0, 0, 1, false, "...|X2X|...", "+++|...|+++" ); // one blocked on three sides rule( 3, 3, 0, 0, 3, false, ".X.|X1X|...", "...|...|+++" ); // three with three edges rule( 3, 3, 0, 0, 3, false, ".+.|+3+|...", "...|...|.X." ); // two with two edges rule( 3, 3, 0, 0, 3, false, ".+.|+2.|...", "...|..X|.X." ); rule( 3, 3, 0, 0, 1, false, "...|+2+|...", ".X.|...|.X." ); // one with one edge rule( 3, 3, 0, 0, 3, false, "...|+1.|...", ".X.|..X|.X." ); // corner through a vertex rule( 3, 3, 1, 1, 3, false, "...|..+|.+.", ".X.|X..|..." ); // straight through a vertex rule( 3, 3, 1, 1, 1, false, "...|+.+|...", ".X.|...|.X." ); // three at a corner rule( 3, 3, 1, 1, 3, false, ".X.|X..|..3", "...|.++|.+." ); // two at a corner rule( 5, 3, 1, 1, 3, true, ".X.|X..|..2|X..|...", "...|...|...|...|.+." ); // one at a corner rule( 3, 3, 1, 1, 3, false, ".X.|X..|..1", "...|..X|.X." ); // line into corner of a three rule( 4, 4, 1, 1, 3, true, ".+..|!...|..3.|....", "....|...+|...+|.+++" ); // line into a corner of a one rule( 4, 4, 1, 1, 3, true, ".+..|X...|..1.|....|", "....|....|...X|..X." ); // line into a corner of a two rule( 5, 5, 1, 1, 3, true, ".+...|X....|..2..|....X|.....", ".....|.....|.....|.....|...+." ); // cornered three diagonal to a one rule( 5, 5, 0, 0, 3, false, ".+...|+3...|.....|...1.|.....", ".....|.....|.....|....X|...X." ); // vertex blocked on three sides rule( 3, 3, 1, 1, 3, false, ".X.|X.X|...", "...|...|.X." ); // coming into a vertex and only one way out rule( 3, 3, 1, 1, 3, true, "...|+.X|.X.", ".+.|.+.|..." ); rule( 3, 3, 1, 1, 3, false, ".X.|+..|.X.", "...|.++|..." ); } private void rule ( int rows, int cols, int anchorRow, int anchorCol, int rotations, boolean doMirror, String patternStr, String ruleStr ) { char[] pattern = new char[ rows * cols ], rule = new char[ rows * cols ]; StringBuffer patternBuf = new StringBuffer( patternStr ), ruleBuf = new StringBuffer( ruleStr ); for ( int n = cols; n < rows * cols; n += cols ) { patternBuf.deleteCharAt( n ); ruleBuf.deleteCharAt( n ); } patternBuf.getChars( 0, rows * cols, pattern, 0 ); ruleBuf.getChars( 0, rows * cols, rule, 0 ); int passes = ( 1 + rotations ) * ( doMirror ? 2 : 1 ); int mirrorAt = ( doMirror ? passes / 2 : 0 ); while ( passes > 0 ) { for ( int r = -anchorRow; r + rows <= _rows + 1; r += 2 ) for ( int c = -anchorCol; c + cols <= _cols + 1; c += 2 ) applyRule( new PositionIterator( r, c, rows, cols ), rows, cols, pattern, rule ); passes--; if ( passes > 0 ) { if ( rotations != 0 ) { char[] rotatedPattern = new char[ rows * cols ]; char[] rotatedRule = new char[ rows * cols ]; for ( int n = 0, i = ( rows - 1 ) * cols; i < rows * cols; i++ ) for ( int j = i; j >= 0; j -= cols, n++ ) { rotatedPattern[n] = pattern[j]; rotatedRule[n] = rule[j]; } pattern = rotatedPattern; rule = rotatedRule; int swap = rows; rows = cols; cols = swap; swap = anchorRow; anchorRow = anchorCol; anchorCol = ( cols - 1 - swap ) % 2; } if ( passes == mirrorAt ) { patternBuf.getChars( 0, rows * cols, pattern, 0 ); ruleBuf.getChars( 0, rows * cols, rule, 0 ); for ( int i = 0; i < rows * cols; i += cols ) for ( int a = i, b = i + cols - 1; a < b; a++, b-- ) { char swap = pattern[a]; pattern[a] = pattern[b]; pattern[b] = swap; swap = rule[a]; rule[a] = rule[b]; rule[b] = swap; } anchorCol = ( anchorCol + ( 1 - cols % 2 ) ) % 2; } } } } private void applyRule ( PositionIterator block, int rows, int cols, char[] pattern, char[] rule ) { for ( int n = 0, r = 0; r < rows; r++ ) for ( int c = 0; c < cols; c++, n++ ) { block.next(); switch ( pattern[n] ) { case '.': // matches anything break; case '!': // matches empty if ( !block.isState( EMPTY ) ) return; break; case 'X': // matches blocked if ( !block.isState( BLOCKED ) ) return; break; case '+': // matches linked if ( !block.isState( LINKED ) ) return; break; case '#': // matches 1, 2, or 3 switch ( block.getState() ) { case '1': case '2': case '3': break; default: return; } break; default: // require exact match by default if ( !block.isState( pattern[n] ) ) return; break; } } block.reset(); for ( int n = 0, r = 0; r < rows; r++ ) for ( int c = 0; c < cols; c++, n++ ) { block.next(); if ( rule[n] != '.' ) block.setState( rule[n] ); } } public void print() { rule( 1, 2, 0, 0, 0, false, ".+", ".-" ); rule( 2, 1, 0, 0, 0, false, "./+", "./|" ); rule( 1, 3, 0, 1, 0, false, "-.-", ".-." ); rule( 3, 1, 1, 0, 0, false, "|/./|", "./|/." ); rule( 2, 2, 0, 0, 0, false, ".!/..", ". /.." ); rule( 2, 2, 0, 0, 0, false, "../!.", "../ ." ); rule( 2, 2, 0, 0, 0, false, "../.!", "../. " ); rule( 1, 1, 0, 0, 0, false, "!", " " ); rule( 1, 2, 0, 0, 0, false, ".X", ". " ); rule( 2, 1, 0, 0, 0, false, "./X", "./ " ); for ( int i = 0; i < 2 * _cols + 3; i++ ) System.out.print( '#' ); System.out.println(); System.out.print( "# " ); for ( int i = 0; i < 2 * _cols - 1; i++ ) System.out.print( ' ' ); System.out.println( " #" ); System.out.print( "# " ); PositionIterator pos = new PositionIterator(); while ( pos.next().isValid() ) { char stateChar = pos.getState(); if ( pos.isHzEdge() ) { System.out.print( ( stateChar == BLOCKED ? ' ' : stateChar ) ); System.out.print( stateChar ); System.out.print( ( stateChar == BLOCKED ? ' ' : stateChar ) ); } else if ( pos.isCell() ) { System.out.print( ' ' ); System.out.print( stateChar ); System.out.print( ' ' ); } else System.out.print( stateChar ); if ( pos.isLastCol() ) { System.out.println( " #" ); System.out.print( "# " ); } } for ( int i = 0; i < 2 * _cols - 1; i++ ) System.out.print( ' ' ); System.out.println( " #" ); for ( int i = 0; i < 2 * _cols + 3; i++ ) System.out.print( '#' ); System.out.println(); } } }