package husacct.analyse.task.reconstruct.mojo; import java.util.*; /* Vertex in a bipartite graph */ class Vertex { boolean matched = false; boolean isLeft = false; int outdegree = 0; int indegree = 0; } class BipartiteGraph { /* * we use a vector to represent a edge list in a directed graph for example, * adjacentList[1] has 2 means there is a edge from point 1 to point 2 */ Vector<Vector<Integer>> adjacentList; /* vertex list */ Vertex vertex[]; /* this list is used to store the augmenting Path we got from matching */ Vector<Integer> augmentPath; /* total number of all points,points in left side and right side */ int points, leftpoints, rightpoints; /* create the graph,points means the toal number of points */ BipartiteGraph(int points, int leftpoints, int rightpoints) { this.leftpoints = leftpoints; this.rightpoints = rightpoints; this.points = points; adjacentList = new Vector<Vector<Integer>>(points); vertex = new Vertex[points]; augmentPath = new Vector<Integer>(); for (int i = 0; i < points; i++) { vertex[i] = new Vertex(); if (i < leftpoints) vertex[i].isLeft = true; adjacentList.add(i, new Vector<Integer>()); } } /* add edge, add an edge to the graph */ public void addedge(int startPoint, int endPoint) { /* insert the edge to the adjacentList of startPoint */ adjacentList.elementAt(startPoint).add(new Integer(endPoint)); /* increase the outdegree of startPoint, indegree of endPoint */ vertex[startPoint].outdegree += 1; vertex[endPoint].indegree += 1; /* * if the edge is from right to left side, mark both start and end as * mached point */ if (isRight(startPoint) && isLeft(endPoint)) { vertex[startPoint].matched = true; vertex[endPoint].matched = true; } } public void removeEdge(int startPoint, int endPoint) { /* find the index of edge in the adjacentList of startPoint */ int index = adjacentList.elementAt(startPoint).indexOf(new Integer(endPoint)); /* remove the edge from adjacentList of startPoint */ if (index > -1) adjacentList.elementAt(startPoint).removeElementAt(index); /* decrease the outdegree of startPoint and indegree of endPoint */ vertex[startPoint].outdegree -= 1; vertex[endPoint].indegree -= 1; /* * if the startPoint is on the right, and its outdegree become zero, * mark the startPoint as unmached */ if (isRight(startPoint) && vertex[startPoint].outdegree == 0) vertex[startPoint].matched = false; /* * if the endPoint is on the left, and its indegree become zero, mark * the endPoint as unmached */ if (isLeft(endPoint) && vertex[endPoint].indegree == 0) vertex[endPoint].matched = false; } /* Change the direction of an edge, e.g. change i -> j to j -> i */ public void reverseEdge(int startPoint, int endPoint) { removeEdge(startPoint, endPoint); addedge(endPoint, startPoint); } /* Reverse all the edges in the augmenting path */ public String XOR() { int start, end; String str = ""; /* the first point of augmenting path */ start = augmentPath.elementAt(0).intValue(); for (int i = 1; i < augmentPath.size(); i++) { end = augmentPath.elementAt(i).intValue(); reverseEdge(start, end); start = end; } return str; } /* do the maximum bipartiture matching */ public String matching() { String str = ""; while (findAugmentPath()) { str += XOR(); } return str; } public boolean findAugmentPath() { augmentPath.removeAllElements(); /* init the path */ /* * use all the unmatched left points as start, see if we can find a * augmenting path */ for (int i = 0; i < leftpoints; i++) { if (vertex[i].matched == false) { if (findPath(i)) return true; else augmentPath.removeAllElements(); /* re init the path */ } } return false; } /* recursive find a path using DFS */ public boolean findPath(int start) { int nextPt, index; /* if the current vertex has no out edge, return false */ if (vertex[start].outdegree == 0) return false; /* insert the current point to the path */ augmentPath.addElement(new Integer(start)); /* * use the pts that the current point is linked to as next point, * recursively call findPath function */ for (int i = 0; i < adjacentList.elementAt(start).size(); i++) { nextPt = adjacentList.elementAt(start).elementAt(i).intValue(); /* if the next point was already in the path, discard it */ if (augmentPath.indexOf(new Integer(nextPt)) > -1) continue; /* find a terminal, add it to the path and return true */ if (vertex[nextPt].matched == false) { augmentPath.addElement(new Integer(nextPt)); return true; } /* otherwise recursive call using depth first search */ else if (findPath(nextPt)) return true; } /* if failed, delete the current pt from path and return false */ index = augmentPath.indexOf(new Integer(start)); augmentPath.removeElementAt(index); return false; } /* indicate whether the current point is in right side */ public boolean isLeft(int pt) { if (pt < leftpoints) return true; else return false; } /* indicate whether the current point is in right side */ public boolean isRight(int pt) { if (pt > leftpoints - 1) return true; else return false; } /* print out the current status of the graph */ @Override public String toString() { String str = ""; for (int i = 0; i < points; i++) { str += "Point "; str += isLeft(i) == true ? "A" + (i + 1) : "G" + (i - leftpoints + 1); str += " is "; str += vertex[i].matched == true ? "MATCHED\n" : "UNMATCHED\n"; for (int j = 0; j < adjacentList.elementAt(i).size(); j++) { int to = adjacentList.elementAt(i).elementAt(j).intValue(); str += " and is connected to points "; str += isLeft(to) ? "A" + (to + 1) : "G" + (to - leftpoints + 1); str += "\n"; } } return str; } }