演算法筆記_139:二分圖的最大權分配(Java)

來源:互聯網
上載者:User

標籤:ret   pac   head   post   16px   class   zha   頂點   時間   

目錄

1 問題描述

2 解決方案

  1 問題描述

何為二分圖的最大權匹配問題?

最大權二分匹配問題就是給二分圖的每條邊一個權值,選擇若干不相交的邊,得到的總權值最大。

 

 

2 解決方案

對於此問題的講解,引用文末參考資料1:

解決這個問題可以用KM演算法。理解KM演算法需要首先理解“可行頂標”的概念。可行頂標是指關於二分圖兩邊的每個點的一個值lx[i]或ly[j],保證對於每條邊w[i][j]都有lx[i]+ly[j]-w[i][j]>=0。如果所有滿足lx[i]+ly[j]==w[i][j]的邊組成的匯出子圖中存在一個完美匹配,那麼這個完美匹配肯定就是原圖中的最大權匹配。理由很簡單:這個匹配的權值之和恰等於所有頂標的和,由於上面的那個不等式,另外的任何匹配方案的權值和都不會大於所有頂標的和。

但問題是,對於當前的頂標的匯出子圖並不一定存在完美匹配。這時,可以用某種方法對頂標進行調整。調整的方法是:根據最後一次不成功的尋找交錯路的DFS,取所有i被訪問到而j沒被訪問到的邊(i,j)的lx[i]+ly[j]-w[i][j]的最小值d。將交錯樹中的所有左端點的頂標減小d,右端點的頂標增加d。經過這樣的調整以後:原本在匯出子圖裡面的邊,兩邊的頂標都變了,不等式的等號仍然成立,仍然在匯出子圖裡面;原本不在匯出子圖裡面的邊,它的左端點的頂標減小了,右端點的頂標沒有變,而且由於d的定義,不等式仍然成立,所以他就可能進入了匯出子圖裡。

初始時隨便指定一個可行頂標,比如說lx[i]=max{w[i][j]|j是右邊的點},ly[i]=0。然後對每個頂點進行類似Hungary演算法的find過程,如果某次find沒有成功,則按照這次find訪問到的點對可行頂標進行上述調整。這樣就可以逐步找到完美匹配了。

值得注意的一點是,按照上述d的定義去求d的話需要O(N^2)的時間,因為d需要被求O(N^2)次,這就成了演算法的瓶頸。可以這樣最佳化:設slack[j]表示右邊的點j的所有不在匯出子圖的邊對應的lx[i]+ly[j]-w[i][j]的最小值,在find過程中,若某條邊不在匯出子圖中就用它對相應的slack值進行更新。然後求d只要用O(N)的時間找到slack中的最小值就可以了。

 

下面代碼所使用的測試資料如:

 

 

具體代碼如下:

package com.liuzhen.practice;import java.util.Scanner;public class Main {    public static int MAX = 100;    public static int n;    public static int[][] value = new int[MAX][MAX];   //給定二分圖的權重值    public static int[] lx = new int[MAX];   //記錄二分圖左半部分頂點的可行頂標    public static int[] ly = new int[MAX];   //記錄二分圖右半部分頂點的可行頂標    public static boolean[] sx = new boolean[MAX];//用於記錄二分圖左半部分頂點是否在最終結果中    public static boolean[] sy = new boolean[MAX];//用於記錄二分圖右半部分頂點是否在最終結果中    public static int[] pre = new int[MAX];  //用於記錄最終結果中頂點y匹配的頂點x        public boolean dfs(int x) {   //採用匈牙利演算法找增廣路徑        sx[x] = true;       //代表左半部分頂點x包含在最終結果中        for(int y = 0;y < n;y++) {            if(!sy[y] && lx[x] + ly[y] == value[x][y]) {                sy[y] = true;   //代表右半部分頂點y包含在最終結果中                if(pre[y] == -1 || dfs(pre[y])) {                    pre[y] = x;                    return true;                }            }        }        return false;    }        public int getKM(int judge) {        if(judge == -1) {  //代表尋找二分圖的最小權匹配            for(int i = 0;i < n;i++)                for(int j = 0;j < n;j++)                    value[i][j] = -1 * value[i][j];  //把權值變為相反數,相當於找最大權匹配        }        //初始化lx[i]和ly[i]        for(int i = 0;i < n;i++) {            ly[i] = 0;            lx[i] = Integer.MIN_VALUE;            for(int j = 0;j < n;j++) {                if(value[i][j] > lx[i])                    lx[i] = value[i][j];            }        }            for(int i = 0;i < n;i++)            pre[i] = -1;      //初始化右半部分頂點y的匹配頂點為-1                for(int x = 0;x < n;x++) { //從左半部分頂點開始,尋找二分圖完美匹配的相等子圖完美匹配            while(true) {                for(int i = 0;i < n;i++) {//每次尋找x的增廣路徑,初始化sx[i]和sy[i]均為被遍曆                    sx[i] = false;                    sy[i] = false;                }                if(dfs(x))  //找到從x出發的增廣路徑,結束迴圈,尋找下一個x的增廣路徑                    break;                //下面對於沒有找到頂點x的增廣路徑進行lx[i]和ly[i]值的調整                int min = Integer.MAX_VALUE;                for(int i = 0;i < n;i++) {                    if(sx[i]) {  //當sx[i]已被遍曆時                        for(int j = 0;j < n;j++) {                            if(!sy[j]) {  //當sy[j]未被遍曆時                                if(lx[i] + ly[j] - value[i][j] < min)                                    min = lx[i] + ly[j] - value[i][j];                            }                        }                    }                }                if(min == 0)                    return -1;                for(int i = 0;i < n;i++) {                    if(sx[i])                        lx[i] = lx[i] - min;                    if(sy[i])                        ly[i] = ly[i] + min;                }            }        }                int sum = 0;        for(int y = 0;y < n;y++) {            System.out.println("y頂點"+y+"和x頂點"+pre[y]+"匹配");            if(pre[y] != -1)                sum = sum + value[pre[y]][y];        }        if(judge == -1)            sum = -1 * sum;        return sum;    }        public static void main(String[] args) {        Main test = new Main();        Scanner in = new Scanner(System.in);        n = in.nextInt();        int k = in.nextInt();   //給定二分圖的有向邊數目        for(int i = 0;i < k;i++) {            int x = in.nextInt();            int y = in.nextInt();            int v = in.nextInt();            value[x][y] = v;        }        System.out.println(test.getKM(1));    }}

 

運行結果:

5100 0 20 1 31 0 22 0 42 2 23 2 13 3 33 4 24 3 84 4 3y頂點0和x頂點2匹配y頂點1和x頂點0匹配y頂點2和x頂點1匹配y頂點3和x頂點4匹配y頂點4和x頂點3匹配17

 

 

 

 

參考資料:

   1.求最大權二分匹配的KM演算法

 2.二分圖最大權匹配-km演算法

 3.二分圖帶權匹配 KM演算法與費用流模型建立

 

演算法筆記_139:二分圖的最大權分配(Java)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.