設平面上分佈着n個白點和n個黑點,每一個點用一對座標(x, y)表示。一個黑點b=(xb,yb)支配一個白點w=(xw, yw)當且僅當xb>=xw和yb>=yw。算法
若黑點b支配白點w,則黑點b和白點w可匹配(可造成一個匹配對)。數組
在一個黑點最多隻能與一個白點匹配,一個白點最多隻能與一個黑點匹配的前提下,求n個白點和n個黑點的最大匹配對數。學習
一看完題目,一開始的思路是先將黑白點分別存入兩個數組中,再對兩個數組分別進行對x和對y的排序,在實際實驗過程當中,發現排序完後數組的下標與點很差對應,這樣就不容易肯定一個點是否已經匹配過。spa
通過了解查閱後發現了最大匹配問題的算法,和本題相似,並且遞歸的操做複雜度遠小於屢次對數組的排序。code
並且過多的排序也形成了算法思路難以理清。決定先學習掌握最大匹配度算法再考慮本題…cdn
在查閱了最大匹配度問題的思想後,發現這是一種遞歸形式的方法,算法須要基於對二分圖的遍歷算法,這就須要學習DFS或者BFS,因此又去複習了一下這兩個算法,在完全掌握了以後終於能夠步入正題了…blog
(dfs和bfs的執行動態圖)排序
http://5b0988e595225.cdn.sohucs.com/images/20171101/f1f45fe9ca37425ba200180be89624b2.gif遞歸
http://5b0988e595225.cdn.sohucs.com/images/20171101/a85c0716fcc847f1915dddfcfd019c01.gifget
理解了最大匹配算法後,發現只要在圖遍歷的基礎上,多借助一個matching數組,用來儲存各匹配點之間的聯繫,經過一些剪枝和判斷就能夠實現。
我選擇了DFS進行最大匹配算法的基礎算法,DFS是對圖作出處理,在空間上須要藉助一張鄰接矩陣,個人想法是將黑白點問題化做圖,再根據題目的要求作出對應的鄰接矩陣,這樣再經過最大匹配就能夠求解出來。
下面主要針對這兩個問題討論並經過具體例子演示最大匹配核心思想。
1、如何將黑白點化做圖:
建立一個結構體
黑白點都看做頂點,只經過color進行區別
2、如何求對應鄰接矩陣:
對儲存全部頂點的結構體數組作兩次循環,若知足題目中黑點xy座標大於白點,即將鄰接矩陣該位置置爲1。
3、具體流程演示:
上圖分析:
經過鄰接表能夠知道,2能控制4,7,8三點
一開始2就控制了4,跳過2點
接着5控制了7,跳過5點
6控制了7,可是7已經被5控制,這時回到5,
5控制了8,跳過5
這時7沒人控制,6控制7,流程結束,匹配度爲3。
1 public class MaxMatching {// 基於DFS 2 3 static int graph[][]; // 鄰接表 默認全爲0 4 static int n; // 節點數 5 static int visit[]; // 是否訪問 6 static int matched[]; // 是否已經匹配,對應的匹配點 7 static vertex V[];//// 結構體數組儲存全部黑白 8 9 public class vertex {// 頂點結構體 10 public int color;// 白:0 黑:1 11 public double Vx; 12 public double Vy; 13 } 14 15 public void Init() { 16 System.out.println("輸入的黑白點總數爲:"); 17 Scanner sc = new Scanner(System.in); 18 n = sc.nextInt(); 19 graph = new int[n][n]; // 鄰接表 默認全爲0 20 visit = new int[n]; // 是否訪問 21 matched = new int[n]; // 是否已經匹配,對應的匹配點 22 V = new vertex[n]; 23 InitGraph();// 初始鄰接矩陣 24 } 25 26 private void InitGraph() { 27 Scanner sc = new Scanner(System.in); 28 for (int i = 0; i < n; i++) {// 輸入黑白點 29 V[i] = new vertex(); 30 System.out.println("please int color/x/y"); 31 V[i].color = sc.nextInt(); 32 V[i].Vx = sc.nextDouble(); 33 V[i].Vy = sc.nextDouble(); 34 } 35 for (int i = 0; i < n; i++) { 36 for (int j = 0; j < n; j++) { 37 if (i != j && (V[i].color == 1) && (V[j].color == 0) && (V[i].Vx > V[j].Vx) && (V[i].Vy > V[j].Vy)) { 38 graph[i][j] = 1; 39 } 40 } 41 } 42 } 43 44 // 顯示匹配結果 45 public void show() { 46 Arrays.fill(visit, 0); 47 for (int i = 0; i < n; i++) { 48 if (visit[i] == 0) { 49 if (matched[i] != -1) { 50 System.out.println("(" + V[i].Vx + "," + V[i].Vy + ")與" + "(" + V[matched[i]].Vx + "," 51 + V[matched[i]].Vy + ")" + "匹配"); 52 visit[i] = 1; 53 visit[matched[i]] = 1; 54 } 55 } 56 } 57 } 58 59 /* 60 * dfs實現, params: x:起始的未匹配點 return: 1:找到增廣路 0:未找到 61 */ 62 public int dfs_solve(int x) { 63 // 找到一個和節點存在邊的點,而且該點在本輪中沒有被訪問過 64 for (int i = 0; i < n; i++) { 65 if (graph[x][i] != 0 && visit[i] == 0) { 66 visit[i] = 1; // 標記爲匹配過 67 // 按照交替路的模式找增廣路,增廣路相對於交替路的特性是就是,第一個節點和最後一個節點都是未匹配過的節點 68 if (matched[i] == -1 || dfs_solve(matched[i]) == 1) { // 直接跳到matched[i]可以保證匹配邊和未匹配邊交替 69 // 說明找到了一個未匹配節點,將全部匹配邊變爲未匹配邊,將全部未匹配邊變爲匹配邊,這樣匹配邊數會加1,這個交換過程經過回溯實現 70 71 matched[x] = i; 72 matched[i] = x; 73 74 System.out 75 .println("(" + V[x].Vx + "," + V[x].Vy + ") 與 " + "(" + V[i].Vx + "," + V[i].Vy + ")" + "匹配"); 76 return 1; 77 } 78 } 79 } 80 return 0; 81 } 82 83 public void hungarian1() { 84 Arrays.fill(matched, -1); 85 int sum = 0; 86 for (int i = 0; i < n; i++) { 87 if (matched[i] == -1) { 88 System.out.println("從 " + "(" + V[i].Vx + "," + V[i].Vy + ")" + " 開始匹配"); 89 Arrays.fill(visit, 0);// 重置瀏覽數組,用來瀏覽鄰接矩陣當前列 90 sum += dfs_solve(i); 91 } 92 } 93 System.out.println("\n"+"最後共有 " + sum + " 匹配項"); 94 show(); 95 } 96 97 public static void main(String[] args) { 98 MaxMatching mm = new MaxMatching(); 99 mm.Init(); 100 mm.hungarian1(); 101 } 102 }