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; // TODO: Auto-generated Javadoc /** * Solution to Borders. * * @author vanb */ public class borders_dinic_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(); /** List of edges out from this node in the level graph. */ public LinkedList levels = 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 */ private 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 */ private 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 ); } } /** * Depth-first Search component of Dinic's algorithm, looking for flows. * * @param node the node * @param snk the sink * @return the flow found */ private int findflow( Node node, Node snk ) { int flow = Integer.MAX_VALUE; node.visited = true; // If we're at the sink, stop. if( node!=snk ) { // Find the best flow we can flow = 0; Edge maxedge = null; // Follow all edges in the level graph with capacity, and which lead to unvisited nodes. for( Edge edge : node.levels ) if( !edge.destination.visited && edge.capacity>0 ) { int newflow = findflow( edge.destination, snk ); if( newflow>0 ) { // Got to figure in the edge's capacity newflow = Math.min( newflow, edge.capacity ); // Is this better? If so, remember it. if( newflow>flow ) { flow = newflow; maxedge = edge; } break; } } // If we got flow, remember how we got it. if( flow>0 ) maxedge.destination.gothere = maxedge; } return flow; } /** Queue for the Dinic algorithm. */ private LinkedList queue = new LinkedList(); /** * Perform the Dinic 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 dinic( 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.levels.clear(); } // First, use BFS to identify the levels graph // Reset the queue // Start at the source queue.clear(); queue.add( src ); src.visited = false; // 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(); if( node.visited ) continue; node.visited = true; // 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 ) { // Build the levels graph node.levels.add( edge ); // Add to the queue queue.add( dest ); } } } // If we were unable to get to the sink, then we're done if( !found ) break; int flow = 0; for(;;) { // Reset the graph for( Node node : nodes ) node.visited = false; // Find the flow flow = findflow( src, snk ); if( flow==0 ) break; // Add that flow to the total total += flow; // Go back along the path, and for each edge, move // the flow from the edge to its dual. for( Node node = snk; node.gothere != null; ) { Edge edge = node.gothere; edge.capacity -= flow; edge.dual.capacity += flow; 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 + dinic( source, sink, nodes ) ); } /** * The main method. * * @param args the arguments */ public static void main( String[] args ) { new borders_dinic_vanb().doit(); } }