import java.io.*; import java.util.*; /** * Science! * @author vanb * * This solution will make EXTENSIVE use of Ford/Fulkerson! * Ford/Fulkerson will hereafter be abbreviated FF. * * We'll build a standard matching graph with a single source node * and a single sink node, and a node for each person and each button. * there will be an edge from the source to each person (the capacity will * be based on the number of matches we're looking for), a link from * a person to a button with capacity 1 if that person can stand on that button, * and a link from each button to the sink (again, the capacity will * be based on the number of matches we're looking for). * * First, we've got to figure out how many permutations are possible. * We'll use FF inside a binary search to nail that down. * * Once we've done that, we'll have to eliminate unused edges - they * can cause us to get stuck in a corner. * * Then, we'll use FF over and over to generate the permutations. */ public class science { public Scanner sc; public PrintStream ps; public static final int MAX = 80; /** * A node of a max flow graph. * * @author vanb */ public class Node { // Has the node been visited? public boolean visited = false; // List of edges from this node public LinkedList edges = new LinkedList(); // If this node has been visited, what edge did we get here from? public Edge from = null; // An ID. Only useful for people. int id = 0; /** * Clear this node for a new graph */ public void clear() { visited = false; edges.clear(); from = null; } } /** * An edge of a max flow graph. * * @author vanb */ public class Edge { Node destination; int capacity; Edge dual; public Edge( Node d, int c ) { capacity = c; destination = d; dual = null; } } /** * 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 ) { 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 ); } /** 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.from = 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.from = 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.from != null; ) { Edge edge = node.from; 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.from != null; ) { Edge edge = node.from; edge.capacity -= min; edge.dual.capacity += min; node = edge.dual.destination; } } // Return the total return total; } // These are all of the nodes we'll need. Node source, sink, people[], buttons[]; /** * Reset the edges from source->people, and from buttons->sink, * to reflect that we're looking for 'cap' matches. * * @param cap Capacity (i.e. number of matches) */ public void resetSourceSink( int cap ) { for( Edge edge : source.edges ) { edge.capacity = cap; edge.dual.capacity = 0; } for( Edge edge : sink.edges ) { edge.capacity = 0; edge.dual.capacity = cap; } } /** * Reset all of the links between people and buttons. */ public void resetPeopleButtons() { for( Node person : people ) { for( Edge edge : person.edges ) { // If this is an edge to a button (i.e. not the source), // and it's been used (i.e. dual capacity is 1), then reset it. if( edge.destination!=source && edge.dual.capacity==1 ) { edge.capacity = 1; edge.dual.capacity = 0; } } } } /** * Do it! * @throws Exception */ public void doit() throws Exception { sc = new Scanner( System.in ); //new File( "science.sample" ) ); ps = System.out; //new PrintStream( new FileOutputStream( "science.vanb" ) ); // Build up some permanent structures for the graph source = new Node(); sink = new Node(); people = new Node[MAX]; buttons = new Node[MAX]; for( int i=0; i nodes = new LinkedList(); for(;;) { int n = sc.nextInt(); if( n==0 ) break; // Build this particular graph. // Start with all the nodes, and the // links from source->people, and from buttons->sink nodes.clear(); source.clear(); sink.clear(); nodes.add( source ); nodes.add( sink ); for( int i=0; ibuttons for( int i=0; i