/* * Same as my other solution except uses Ford-Fulkerson * for the flow. */ import java.util.*; import java.io.*; public class AbstractArt_ff_font { public static void main(String[] args) throws Exception { PrintWriter out = new PrintWriter(System.out); new AbstractArt_ff_font(new FastScanner(System.in), out); out.close(); } int N, M; char[][] grid, backup; int[] dr = {-1,0,1,0}; int[] dc = {0,-1,0,1}; int minVertexCover; int getNode(int i, int j) { return i * M + j; } ArrayList[] adj; void addEdge(int i, int j, boolean isMatched) { adj[i].add(new Edge(i,j,isMatched)); adj[j].add(new Edge(j,i,isMatched)); } // Run flow to find the vertex cover. int matchOppositeColors() { FordFulkerson flowSolver = new FordFulkerson(N*M); for (int i=0; i(); int minVertexCover = flowSolver.run(); // Save the matching for (int i=0; i q = new ArrayDeque<>(); boolean[] side = new boolean[N*M]; for (int i=0; i 0) { int x = q.poll(); if (side[x]) { // Traverse unmatched edges for (Edge e : adj[x]) { if (!Z[e.j] && !e.matchedEdge) { q.add(e.j); Z[e.j] = true; } } } else { // Traverse matched edges for (Edge e : adj[x]) { if (!Z[e.j] && e.matchedEdge) { q.add(e.j); Z[e.j] = true; } } } } boolean[] inVertexCover = new boolean[N*M]; for (int x=0; x canKeep = new TreeSet<>(); for (int i=0; i[] adj; boolean[] seen; public FordFulkerson(int numNodes) { this.source = numNodes++; this.sink = numNodes++; this.numNodes = numNodes; seen = new boolean[numNodes]; adj = new ArrayList[numNodes]; for (int i=0; i(); } } void addEdge(int i, int j, int cap) { Edge fwd = new Edge(i, j, cap); Edge rev = new Edge(j, i, 0); fwd.rev = rev; rev.rev = fwd; adj[i].add(fwd); adj[j].add(rev); } int dfs(int curNode, int flowCap) { if (seen[curNode] || flowCap == 0) return 0; seen[curNode] = true; if (curNode == sink) return flowCap; for (Edge e : adj[curNode]) { int amtSent = dfs(e.j, Math.min(e.cap, flowCap)); if (amtSent > 0) { e.cap -= amtSent; e.rev.cap += amtSent; return amtSent; } } return 0; } int run() { int totalFlow = 0; int flowSent = -1; while (flowSent != 0) { Arrays.fill(seen, false); flowSent = dfs(source, Integer.MAX_VALUE); totalFlow += flowSent; } return totalFlow; } class Edge { int i, j, cap; Edge rev; public Edge(int i, int j, int cap) { this.i = i; this.j = j; this.cap = cap; this.rev = null; } } } class FastScanner { private InputStream stream; private byte[] buf = new byte[1024]; private int curChar; private int numChars; public FastScanner(InputStream stream) { this.stream = stream; } int read() { if (numChars == -1) throw new InputMismatchException(); if (curChar >= numChars) { curChar = 0; try { numChars = stream.read(buf); } catch (IOException e) { throw new InputMismatchException(); } if (numChars <= 0) return -1; } return buf[curChar++]; } boolean isSpaceChar(int c) { return c==' '||c=='\n'||c=='\r'||c=='\t'||c==-1; } boolean isEndline(int c) { return c=='\n'||c=='\r'||c==-1; } int nextInt() { return Integer.parseInt(next()); } long nextLong() { return Long.parseLong(next()); } double nextDouble() { return Double.parseDouble(next()); } String next() { int c = read(); while (isSpaceChar(c)) c = read(); StringBuilder res = new StringBuilder(); do { res.appendCodePoint(c); c = read(); } while(!isSpaceChar(c)); return res.toString(); } String nextLine() { int c = read(); while (isEndline(c)) c = read(); StringBuilder res = new StringBuilder(); do { res.appendCodePoint(c); c = read(); } while(!isEndline(c)); return res.toString(); } }