匈牙利算法c语言代码,漫谈匈牙利算法
匈牙利算法是由匈牙利數學家Edmonds于1965年提出,因而得名。匈牙利算法是基于Hall定理中充分性證明的思想,它是部圖匹配最常見的算法,該算法的核心就是尋找增廣路徑,它是一種用增廣路徑求二分圖最大匹配的算法。(來自百度百科)
淺顯的講,就是尋找二部圖的最大匹配,先寫出幾個概念;
二部圖:二分圖又稱作二部圖,是圖論中的一種特殊模型。 設G=(V,E)是一個無向圖,如果頂點V可分割為兩個互不相交的子集(A,B),并且圖中的每條邊(i,j)所關聯的兩個頂點i和j分別屬于這兩個不同的頂點集(i in A,j in B),則稱圖G為一個二分圖。(來自百度百科)
6B974A22-EFFB-4813-BF81-6149A037675A.png
當子圖中沒有任何一個同時連接Xi和Yj的同一頂點的時候我們稱為二部圖的一個匹配,記為集合M
什么是最大匹配呢 如下圖
9C2492EA-5019-4BC5-A84D-A2856E55E603.png
當匹配集合中的邊數達到最大的時候,這就是一個最大匹配,如上圖;
再提出幾個個概念
未覆蓋點:就是在集合M中沒有邊連接到的點稱為未覆蓋點。
完美匹配:當M是二部圖的最大匹配且沒有未覆蓋點的時候,則這個集合M就是二部圖的完美匹配。上圖中的匹配就是完美匹配,在符合最大匹配的同時,M集合中的邊覆蓋了二部圖的所有點。
最后一個最關鍵的概念:增廣路徑 ;
匈牙利算法的核心就是不停的尋找增廣路徑來擴充匹配集合M,什么是增廣路經呢?(來自百度百科)
1-P的路徑長度必定為奇數。
2-起點在左,終點在右。
3-路徑中的點左右交替出現。
4-只有起點和終點是未覆蓋點,其他點都配對。
5 -對增廣路徑編號,所有奇數的邊都不在M中,偶數邊在M中。
6 -對增廣路徑取反得到的匹配比原來匹配多一個。
我們給出實例來理解。
C33D49B0-2527-48FF-A442-762BEB630E01.png
我們尋找如上圖的最大匹配;首先M集合為空(即沒有邊在里面),然后開始從X1尋找增廣路,遵循2的原則我們只能在Yi中找,找到Y1,(X1,Y1 )這條路徑,滿足1-5的條件,取反,將(X1,Y1 )這條路徑加入到M中。
2E79CD71-536E-4171-9156-844F956A8EF5.png
接著,我們找到X2點,遵循原則,找到Y1,Y1不是未覆蓋點,這個時候我們有兩種選擇,一個是深度搜索,一個是廣度搜索,我們采用深度優先,雖然Y1不是未覆蓋點,(X2,Y1)不是增廣路,但是Y1連著X1,X1又和Y3相連,我們考慮( X2,Y1,X1,Y3 )這條路徑,奇數?左右交替?起終點未覆蓋?奇路徑不屬于M偶路徑屬于?滿足所有增廣路條件,所以這是一條增廣路徑,然后取反,得到如下圖。
5213FA71-C31A-4461-9A5F-54AF59BF4B02.png
現在M集合中的路徑有兩條了,由于我們找到了增廣路徑,使得M中的邊數量增加。所以增廣路徑是匈牙利算法的核心,每找到一條增廣路徑,意味這M集合中邊的數量就會增加1,當找不到增廣路徑的時候,這個時候M中邊的數量就是我們二部圖的最大匹配數量。我們是怎樣找到這條路徑的呢,從X2開始尋找,我們先找到Y1,Y1不是未覆蓋點,我們考慮Y1的原有匹配點X1,從X1開始尋找增廣路,找到了Y3,當X1有增廣路的時候,那么加上(X1,Y1)(X2,Y1)這兩條路經,依然滿足增廣路條件。所以基于我們上面的理解可以給出尋找增廣路的偽代碼
while(找到Xi的關聯頂點Yj){
if(頂點Yj不在增廣路徑上){
將Yj加入增廣路
if(Yj是未覆蓋點或者Yj的原匹配點Xk能找到增廣路徑){ //擴充集合M
將Yj的匹配點改為Xi;
返回true
}
}
返回false
}
從X2開始尋找是基于深度優先的,如果是基于廣度優先呢?那么X2就會找到Y2。
給出C語言代碼
typedef struct tagMaxMatch{
int edge[COUNT][COUNT];
bool on_path[COUNT];
int path[COUNT];
int max_match;
}GRAPH_MATCH;
void outputRes(int *path){
for (int i = 0 ; i
printf("%d****%d\n",i,*(path+i)); //Yj在前 Xi在后
}
}
void clearOnPathSign(GRAPH_MATCH *match){
for (int j = 0 ; j < COUNT ; j++) {
match->on_path[j] = false;
}
}
//dfs算法
bool FindAugPath(GRAPH_MATCH *match , int xi){
for (int yj = 0 ; yj < COUNT; yj++) {
if ( match->edge[xi][yj] == 1 && !match->on_path[yj]) { //如果yi和xi相連且yi沒有在已經存在的增廣路經上
match->on_path[yj] = true;
if (match->path[yj] == -1 || FindAugPath(match,match->path[yj])) { // 如果是yi是一個未覆蓋點或者和yi相連的xk點能找到增廣路經,
match->path[yj] = xi; //yj點加入路徑;
return true;
}
}
}
return false;
}
void Hungary_match(GRAPH_MATCH *match){
for (int xi = 0; xi
FindAugPath(match, xi);
clearOnPathSign(match);
}
outputRes(match->path);
}
int main() {
GRAPH_MATCH *graph = (GRAPH_MATCH *)malloc(sizeof(GRAPH_MATCH));
for (int i = 0 ; i < COUNT ; i++) {
for (int j = 0 ; j < COUNT ; j++) {
graph->edge[i][j] = 0;
}
}
graph->edge[0][1] = 1;
graph->edge[0][0] = 1;
graph->edge[1][1] = 1;
graph->edge[1][2] = 1;
graph->edge[2][1] = 1;
graph->edge[2][0] = 1;
graph->edge[3][2] = 1;
for (int j = 0 ; j < COUNT ; j++) {
graph->path[j] = -1;
graph->on_path[j] = false;
}
Hungary_match(graph);
}
總結
以上是生活随笔為你收集整理的匈牙利算法c语言代码,漫谈匈牙利算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c语言获取安卓弹窗,Android实现信
- 下一篇: c语言如何获取串口列表,如何通过串口来读