import java.io.PrintStream; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Scanner; /** * Solution to Borders. * * @author vanb */ public class borders_vanb { /** The input. */ private Scanner sc = new Scanner( System.in ); /** The output. */ private PrintStream ps = System.out; /** Number of rows in the image. */ private int n; /** The Number of columns in the image. */ private int m; /** The image. */ private char image[][]; /** The index of the graph node for each region. */ private int index[][]; /** * Floodfill. * * @param i the i of an (i,j) coordinate * @param j the j of an (i,j) coordinate * @param pixel the pixel value * @param value the value for the index array * @return true, if not yet floodfilled (and floodfilled by this call) */ private boolean floodfill( int i, int j, char pixel, int value ) { boolean filled = false; if( i>=0 && i=0 && j edges = new LinkedList(); /** If this node has been visited, what edge did we get here from? */ public Edge gothere = null; /** The name. */ public String name; /** * Instantiates a new node. * * @param name the name */ public Node( String name ) { this.name = name; } } /** * An edge of a max flow graph. * * @author vanb */ public class Edge { /** The destination. */ Node destination; /** The capacity. */ int capacity; /** The dual. */ Edge dual; /** * Instantiates a new edge. * * @param d the Destination Node * @param c the Capacity */ public Edge( Node d, int c ) { capacity = c; destination = d; dual = null; } } /** Remember pairs of nodes that have already been linked, so we don't re-make those links. */ private HashSet linked = new HashSet(); /** * Create a link between two nodes of a max flow graph. * * @param n1 From node * @param n2 To node * @param cost Cost to go from n1 to n2 */ public void link( Node n1, Node n2, int cost ) { // Describe a link in a String String key = n1.name + "->" + n2.name; // Make sure we haven't already made this link if( !linked.contains(key) ) { Edge e12 = new Edge( n2, cost ); Edge e21 = new Edge( n1, 0 ); e12.dual = e21; e21.dual = e12; n1.edges.add( e12 ); n2.edges.add( e21 ); linked.add( key ); } } /** Queue for the Ford/Fulkerson algorithm. */ public LinkedList queue = new LinkedList(); /** * Perform the Ford/Fulkerson algorithm on a graph. * * @param src Source node * @param snk Sink node * @param nodes The graph, represented as a list of nodes * @return The max flow from the source to the sink */ public int fordfulkerson( Node src, Node snk, List nodes ) { int total = 0; // Keep going until you can't get from the source to the sink for(;;) { // Reset the graph for( Node node : nodes ) { node.visited = false; node.gothere = null; } // Reset the queue // Start at the source queue.clear(); queue.add( src ); src.visited = true; // Have we found the sink? boolean found = false; // Use a breadth-first search to find a path from the source to the sink while( queue.size()>0 ) { Node node = queue.poll(); // have we found the sink? If so, break out of the BFS. if( node==snk ) { found = true; break; } // Look for edges to traverse for( Edge edge : node.edges ) { Node dest = edge.destination; // If this destination hasn't been visited, // and the edge has capacity, // put it on the queue. if( edge.capacity>0 && !dest.visited ) { // Node has been visited dest.visited = true; // Remember the edge that got us here dest.gothere = edge; // Add to the queue queue.add( dest ); } } } // If we were unable to get to the sink, then we're done if( !found ) break; // Otherwise, look along the path to find the minimum capacity int min = Integer.MAX_VALUE; for( Node node = snk; node.gothere != null; ) { Edge edge = node.gothere; if( edge.capacity < min ) min = edge.capacity; node = edge.dual.destination; } // Add that minimum capacity to the total total += min; // Go back along the path, and for each edge, move the min // capacity from the edge to its dual. for( Node node = snk; node.gothere != null; ) { Edge edge = node.gothere; edge.capacity -= min; edge.dual.capacity += min; node = edge.dual.destination; } } // Return the total return total; } /** * Determine if this region is linkable to a region under consideration. * * @param rtest the index of the region we're testing * @param rconsider the index of the region we're considering * @return true, if we can link the region we're testing to the region we're considering */ private boolean linkable( int rtest, int rconsider ) { // Check to make sure index is >=0 (that is, (i,i) is not on a border region) // and also that (i,j) is in a different region that the one we're looking at. return rtest>=0 && rtest!=rconsider; } /** * Do it! */ private void doit() { n = sc.nextInt(); m = sc.nextInt(); image = new char[n][m]; index = new int[n][m]; // Read in the image, initialize the index array for( int i=0; i nodes = new ArrayList(nodenum+2); Node source = new Node( "source" ); nodes.add( source ); Node sink = new Node( "sink" ); nodes.add( sink ); Node regions[] = new Node[nodenum]; for( int i=0; i=0 ) { if( image[i][j]=='0' ) { link( source, regions[index[i][j]], 1 ); if( linkable( index[i-1][j], index[i][j] ) ) link( regions[index[i][j]], regions[index[i-1][j]], 1 ); if( linkable( index[i][j-1], index[i][j] ) ) link( regions[index[i][j]], regions[index[i][j-1]], 1 ); } else { link( regions[index[i][j]], sink, 1 ); if( linkable( index[i-1][j], index[i][j] ) ) link( regions[index[i-1][j]], regions[index[i][j]], 1 ); if( linkable( index[i][j-1], index[i][j] ) ) link( regions[index[i][j-1]], regions[index[i][j]], 1 ); } } } ps.println( count + fordfulkerson( source, sink, nodes ) ); } /** * The main method. * * @param args the arguments */ public static void main( String[] args ) { new borders_vanb().doit(); } }