程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 帶權的二分圖的最優匹配KM算法

帶權的二分圖的最優匹配KM算法

編輯:C++入門知識

[cpp]   /*********************************************************  算法引入:  給定一個完全二分圖G=(X∪Y,X×Y),其中邊(x,y)有權w(x,y);  要找一個從X到Y具有最大權和的匹配M,即為二分圖的最優匹配問題;  KM(Kuhn_Munkras)算法求的是完備匹配下的最大權匹配;    算法思想:  KM算法是通過給每個頂點一個標號(叫做頂標)來把求最大權匹配的問題轉化為求完備匹配的問題的;  設頂點Xi的頂標為A[i],頂點Yi的頂標為B[i],頂點Xi與Yj之間的邊權為w[i,j];  在算法執行過程中的任一時刻,對於任一條邊(i,j),A[i]+B[j]>=w[i,j]始終成立;  初始A[i]為與Xi相連的邊的最大邊權,B[j]=0;    KM算法的正確性基於以下定理:  設G(V,E)為二分圖,G'(V,E')為該二分圖的子圖;  如果對於G'中的任何邊<x,y>滿足, A(x)+ B(y)==W[x,y];  則稱G'(V,E')為G(V,E)的等價子圖或相等子圖(是G的生成子圖);  若由二分圖中所有滿足A[i]+B[j]=w[i,j]的邊(i,j)構成的子圖(稱做相等子圖)有完備匹配;  那麼這個完備匹配就是二分圖的最大權匹配;    因為對於二分圖的任意一個匹配,如果它包含於相等子圖;  那麼它的邊權和等於所有頂點的頂標和;  如果它有的邊不包含於相等子圖,那麼它的邊權和小於所有頂點的頂標和(即不是最優匹配);  所以相等子圖的完備匹配一定是二分圖的最大權匹配;    相等子圖包含原圖的所有的點,相等子圖一定可以找到完備匹配;  相等子圖的完備匹配只需加一些虛擬點可以擴充為完美匹配(記為M);  完美匹配是包含了所有點的匹配,那麼所有點的頂點的標號值都包括進來了;  雖然有些點是0,在這個狀態下,把相等子圖的標號一一對應的標到原圖上去;  原圖的任意一個匹配最多只能包含原圖的所有頂點;  即任何匹配的權和不可能超過所有標號的和,所以M的和必然是最優的;    算法改進:  給每個Y頂點一個"松弛量"函數slack;  每次開始找增廣路時初始為無窮大;  在尋找增廣路的過程中,檢查(i,j)時,如果它不在相等子圖中;  則讓slack[j]=min(原值,A[i]+B[j]-W[i,j]);  這樣在修改頂標時,取所有的不在交錯樹中的Y頂點的slack值中的最小值作為d值即可;    算法過程:  ①初始化可行頂標的值;  ②用匈牙利算法尋找完備匹配;  ③若未找到完備匹配則修改可行頂標的值;  ④重復②③直到找到相等子圖的完備匹配;  **********************************************************/   #include<iostream>   #include<cstring>   #include<cstdlib>   #include<cstdio>   #include<climits>   #include<algorithm>   using namespace std;      const int N = 1000;   const int INF = 0xffffff;   int w[N][N];//權值   int lx[N],ly[N]; //頂標   int linky[N];//記錄與i匹配的頂點   int visx[N],visy[N];   int slack[N];//松弛量   int nx,ny;//二分圖兩邊的頂點數      void init()   {       memset(linky,-1,sizeof(linky));//記錄與i匹配的頂點       memset(ly,0,sizeof(ly));///初始化頂標y為0       for(int i = 0; i < nx; i++)           for(int j = 0,lx[i] = -INF; j < ny; j++)           {               if(w[i][j] > lx[i])                   lx[i] = w[i][j];///初始化頂標x為與頂點Xi關聯的邊的最大權           }      }      bool find(int x)//匈牙利算法   {       visx[x] = true;       for(int y = 0; y < ny; y++)       {           if(visy[y])               continue;           int t = lx[x] + ly[y] - w[x][y];//若t==0,則為最大權匹配;              if(t==0)           {               visy[y] = true;               if(linky[y]==-1 || find(linky[y]))               {                   linky[y] = x;                   return true;        //找到增廣軌               }           }              else if(slack[y] > t)               slack[y] = t;       }       return false;                   //沒有找到增廣軌(說明頂點x沒有對應的匹配,與完備匹配(相等子圖的完備匹配)不符)   }      int KM()                //返回最優匹配的值   {       init();       for(int x = 0; x < nx; x++)       {           for(int i = 0; i < ny; i++)               slack[i] = INF;//松弛函數初始化為無窮大              while(1)           {               memset(visx,0,sizeof(visx));               memset(visy,0,sizeof(visy));               if(find(x))                     //找到增廣軌,退出                   break;               int d = INF;               for(int i = 0; i < ny; i++)          //沒找到,對l做調整(這會增加相等子圖的邊),重新找               {                   if(!visy[i] && d > slack[i])                       d = slack[i];               }               for(int i = 0; i < nx; i++)//修改x的頂標               {                   if(visx[i])                       lx[i] -= d;               }               for(int i = 0; i < ny; i++)//修改y的頂標               {                   if(visy[i])                       ly[i] += d;                   else                       slack[i] -= d;//修改頂標後,不在交錯樹中的y頂點的slack值都要減去d;               }           }          }          int result = 0;       for(int i = 0; i < ny; i++)       {           if(linky[i]>-1)               result += w[linky[i]][i];       }       return result;   }      int main()   {   www.2cto.com     //freopen("C:\\Users\\Administrator\\Desktop\\kd.txt","r",stdin);       while(~scanf("%d%d",&nx,&ny))       {           if(!nx||!ny)               break;           int a,b,c;           while(scanf("%d%d%d",&a,&b,&c),a+b+c)           {               w[a][b]=c;           }           printf("%d\n",KM());       }       return 0;   }    

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved