// Solution to Sofa, So Good // // Basically just copied from TopCoder: // http://community.topcoder.com // /tc?module=Static&d1=tutorials&d2=hungarianAlgorithm // but changing some variable names and negating all input values to convert // maximize to minimize. import java.util.*; public class SofaB { public static Scanner in; public static int n, caseNum; public static int[][] fr, up, idle; // framing, upholstering, idle times public static int[][] c; // parameter public static int matched, maxwt; // # edges and weight of matching public static int[] wlabel, slabel; // worker, sofa labels public static int[] wmatch, smatch; // nbrs in matching public static boolean[] s,t; // sets used in augmentation public static int[] prev, slack, slackx; public static void main(String[] args) { in = new Scanner(System.in); caseNum = 0; while(true) { n = in.nextInt(); if (n == 0) break; caseNum++; fr = new int[n][n]; up = new int[n][n]; idle = new int[n][n]; for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) fr[i][j] = -in.nextInt(); // negative since max matching for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) up[i][j] = -in.nextInt(); c = fr; solve(); // Save results so we can re-use wmatch array: int phase1[] = new int[n]; for (int i = 0; i < n; i++) { phase1[i] = wmatch[i]; } adjust(); c = up; solve(); int totidle = 0; System.out.println("Case " + caseNum + ":"); for (int i = 0; i< n; i++) { System.out.print("Worker " + (i+1) + ": "); System.out.print((1+phase1[i])+" "+(1+wmatch[i])+" "); System.out.println(-fr[i][phase1[i]] -up[i][wmatch[i]]); totidle+=(idle[i][wmatch[i]]); } System.out.println("Total idle time: " + totidle); } } // Algorithm/code borrowed from: // http://community.topcoder.com/ // tc?module=Static&d1=tutorials&d2=hungarianAlgorithm // For a while I added my own comments to explain to myself what // was going on... public static void solve() { // Initial empty matching: maxwt = 0; // max so far (which we negate to get min) matched = 0; // number of edges in current matching wmatch = new int[n]; Arrays.fill(wmatch,-1); smatch = new int[n]; Arrays.fill(smatch,-1); // Initial feasible labeling: wlabel = new int[n]; slabel = new int[n]; Arrays.fill(wlabel,0); Arrays.fill(slabel,0); // sofa labels all 0 for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) wlabel[i] = Math.max(wlabel[i],c[i][j]); // worker labels = max edge // The hard part: augment(); } public static void adjust() { // Add time to the appropriate entries in "up" based upon // framing results. If worker i finished framing sofa // wmatch[i] at time t1, and worker smatch[j] finishes // framing sofa j at time t2, worker i must wait an additional // t2-t1 minutes to frame sofa j if t2 > t1, 0 otherwise. for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { idle[i][j] = Math.max(0,fr[i][wmatch[i]]-fr[smatch[j]][j]); up[i][j] -= idle[i][j]; } } } public static void augment() { if (matched==n) return; int x, y, root=-1; int q[] = new int[n]; int wr=0,rd=0; s = new boolean[n]; t = new boolean[n]; Arrays.fill(s,false); Arrays.fill(t,false); prev = new int[n]; Arrays.fill(prev,-1); // Look for an exposed vertex in the equality graph: for (x = 0; x < n; x++) { if (wmatch[x] == -1) { q[wr++] = root = x; // root of alt. tree; queued for BFS prev[x] = -2; // no parent in tree since root s[x] = true; // place x in s break; // found root of aug tree--done } } // Build alt. path tree. At some point we are going to // need to find smallest difference between edge cost // and label cost; slack helps us find that and slackx // helps us remember where the label changes have to take place. slack = new int[n]; slackx = new int[n]; for (y = 0; y < n; y++) { slack[y] = wlabel[root]+slabel[y]-c[root][y]; slackx[y] = root; } while (true) //main cycle { while (rd < wr) //building tree with bfs cycle { x = q[rd++]; //current vertex from X part for (y = 0; y < n; y++) //iterate through all edges in equality graph if (c[x][y] == wlabel[x] + slabel[y] && !t[y]) { if (smatch[y] == -1) break; //an exposed vertex in Y found, so //augmenting path exists! t[y] = true; //else just add y to T, q[wr++] = smatch[y]; //add vertex smatch[y], which is matched //with y, to the queue add(smatch[y], x); //add edges (x,y) and (y,smatch[y]) to the tree } if (y < n) break; //augmenting path found! } if (y < n) break; //augmenting path found! update(); //augmenting path not found, so improve labeling wr = rd = 0; for (y = 0; y < n; y++) //in this cycle we add edges that were added to the equality //graph as a result of improving the labeling, we add // edge (slackx[y], y) to the tree if and only if !T[y] // && slack[y] == 0, also with this edge we add another one // (y, smatch[y]) or augment the matching, if y was exposed if (!t[y] && slack[y] == 0) { if (smatch[y] == -1) //exposed vertex in Y found - augmenting path exists! { x = slackx[y]; break; } else { t[y] = true; //else just add y to T, if (!s[smatch[y]]) { q[wr++] = smatch[y]; //add vertex smatch[y], which is matched with //y, to the queue add(smatch[y], slackx[y]); //and add edges (x,y) and (y, //smatch[y]) to the tree } } } if (y < n) break; //augmenting path found! } if (y < n) //we found augmenting path! { matched++; //increment matching //in this cycle we inverse edges along augmenting path for (int cx = x, cy = y, ty; cx != -2; cx = prev[cx], cy = ty) { ty = wmatch[cx]; smatch[cy] = cx; wmatch[cx] = cy; } augment(); //recall function, go to step 1 of the algorithm } } public static void update() { int x, y, delta = Integer.MAX_VALUE; // delta = smallest slack value in the complement of // neighborhood of workers in equality graph for (y = 0; y < n; y++) { if (!t[y]) delta = Math.min(delta,slack[y]); } // Apply delta to edges not in equality graph: for (x = 0; x < n; x++) if (s[x]) wlabel[x] -= delta; for (y = 0; y < n; y++) if (t[y]) slabel[y] += delta; for (y = 0; y < n; y++) if (!t[y]) slack[y] -= delta; } public static void add(int x, int prevx) { s[x] = true; prev[x] = prevx; for (int y = 0; y < n; y++) if (wlabel[x]+slabel[y] - c[x][y] < slack[y]) { slack[y] = wlabel[x]+slabel[y]-c[x][y]; slackx[y] = x; } } // For debugging: public static void show() { System.out.println("Frame:"); System.out.print(" "); for (int i = 0; i < n; i++) System.out.printf("%5d",i); System.out.println(); for (int i = 0; i < n; i++) { System.out.printf("%2d ",i); for (int j = 0; j < n; j++) System.out.printf("%5d",fr[i][j]); System.out.println(); } System.out.println("Upholster:"); System.out.print(" "); for (int i = 0; i < n; i++) System.out.printf("%5d",i); System.out.println(); for (int i = 0; i < n; i++) { System.out.printf("%2d ",i); for (int j = 0; j < n; j++) System.out.printf("%5d",up[i][j]); System.out.println(); } } }