ディレクトリ
ハンガリーアルゴリズム(ハンガリー法エドモンズ)
試合などの任意のMが開始されます。(好ましくはM =∅)。
M X各頂点が停止飽和場合①(Mは完全一致です)。∅{U}、T = - S <:それ以外の場合、Xの不飽和M-頂点U、今日取ります。
そうでない場合は、N(S)⊃Tは、次のステップに進み、②もしN(S)= Tは、アルゴリズムは終了する(完璧な一致)が、停止します。
y∈N(S)\ T、Yが飽和した場合M-提供yz∈Mを取る③、次にS =S∪(z)は、T =T∪{Y}は、②のステップに行こう、そうでない場合、Yは不飽和M-、M-拡張プレゼンス経路Pので、M = M△E(P)は、①のステップに行くこと。
M-飽和:エッジuv∈Mを、uおよびvは、M-飽和指すように言われています。
不飽和M-:ポイントに関連付けられているすべてのエッジがMに属さないwは、Mが点M-不飽和と呼ばれています。
Oセットポイントは、Sを設定し、Sは、隣接する頂点図の全ての点の集合である:N(S)。
M-は方法を互い違い:P側がM- pがインターレース方法であると呼ばれる、M側とMに属するが、エッジがGに交互に表示されない場合は、pは、Gの経路です。
Mエッジに属するエッジはM(すなわち、一致すると、一致する側)P上の代替、ない:M-通路Pは、(パスを増強する)拡張することができる不飽和M-の始点と終点。
E(P):M-チャネルセットは、(増強パス)Pエッジを拡張することができます。
M△E(P)=(M∪E(P))\(M∩E(P))
推測することができる次の三つの結論で通過(増補パス)を拡大して定義します。
パスの数(1)Pは、必ずしも奇数であり、第1の側縁と最後のMに属しません。
(2)M及びPは、大きなMと一致する排他的OR演算することができ、以前の一致よりもM上。
Mパスを増強する場合にのみと存在しない場合(3)MはGの最大マッチングです。
例1は完全に一致するものがあります
M所与図から= {x1y2、x5y4}はハンガリーアルゴリズムと完全に一致するために、開始します。
1)MがM-うち各Xの頂点、Xのために飽和していないが、不飽和X3を頂点S = {X3}ので、T = ∅、 N(S)= {Y1、 Y2、Y3}、N(S )⊃T、採取y3∈N(S)\ T、Y3は、不飽和M-であり、M-検索拡張パスP = X3Y3、その結果、△E M = M(P)= {X1Y2、X3Y3、x5y4}
2)Mは、不飽和X2頂点M-うち各Xの頂点、Xのために飽和していないS = {X2}、T =そう ∅、 N(S)= {Y1} 、N(S)⊃T、 その結果、△E M = M(P)= {X1Y2、y1∈N(S)が\ T、Y 1が不飽和M-であり、広大なパスP = x2y1を見つけるM-取るX2Y1、X3Y3、x5y4}
3)Mは、不飽和X4頂点M-うち各Xの頂点、Xのために飽和していないS = {X4}、T =そう ∅、 N(S)= {Y2} 、N(S)⊃T、 y2∈N(S)を取る\ T、Mが飽和するY2、及びy2x1∈M(y2x1 = x1y2)、S∪ので= {X1} S = { X1、X4}、T = {Y2} =T∪ { Y2 }。
N(S)= {Y2、Y4 }、N(S)⊃T、y4∈N(S)\ T、Y4 M-が飽和されとり、y4x5∈M(y4x5 = x5y4)、その結果、S =S∪ X5 = {} { X1、 X4、X5 }、T = {Y4T∪} = {Y2、Y4}。
N(S)= {Y2、Y4、Y5 }、Ny5∈N(S)\ T、不飽和M-のY5を採取(S)⊃T、M-は、Mを聞かせて、膨張経路P = x4y2x1y4x5y5を見つけます。 △M E =(P)= { X1Y2、X2Y1、X3Y3、x5y4 } {△x4y2、y2x1、X1Y4、y4x5、x5y5} = {X1Y4、X2Y1、X3Y3、x4y2、x5y5}、Mの各頂点Xを含みます、ストップ。
(プッシュダウンでき、Y5は、X5の導入によるものである見つけるためのスケーラブルな方法は、X5はY4の導入に起因して、Y4は、X1の導入によるもので、x1はY2の導入によるものである、y2が始まる取らX4によるものです)
注:手書き検査、XおよびY接続から横断する必要がない場合、例えば、1)が選択され、X3Y1、明らかにのみ接続X2 Y1のためされていません。もちろん、あなたが達成するために、コードを見たとき、それはそう単純では手書きではありません、あなたは深いいくつかの剪定をしたを検索する必要があるとき手書き、コードを見て我慢してください。
手カウント検証:Mのみ接続され、空、X4及びY2、M = {x4y2}であり; X3及びY1、Y2、接続Y3、Y1とY2飽和M-; X2及びY1は、M = {x2y1、x4y2は}に接続されています。 、X3とY3のみ一致し、M = {x2y1、X3Y3、x4y2}; X1及びY2、Y4が接続され、Y2が飽和しているM-、X1及びY4のみ一致し、M = {x1y4、x2y1、X3Y3、 x4y2}; X5とY5を残り、双方が一致するように2つの点があるので、M = {x1y4、x2y1、X3Y3、x4y2、x5y5}のための完全な一致があります。
例2なしに完璧にマッチ
M与えられた図から=∅ハンガリーのアルゴリズムと完全に一致するために、開始します。
1)M没有饱和X的每个顶点,取X中M-不饱和的顶点x1,令S={x1},T=∅,则N(S)={y2,y3},N(S)⊃T, 取y2∈N(S)\T,y2为M-不饱和的,找到M-可扩路P=x1y2,令M=M△E(P)={ x1y2}
2)M没有饱和X的每个顶点,取X中M-不饱和的顶点x2,令S={x2},T=∅,则N(S)={y1,y2,y4,y5},N(S)⊃T, 取y1∈N(S)\T,y1为M-不饱和的,找到M-可扩路P=x2y1,令M=M△E(P)={ x1y2,x2y1}
3)M没有饱和X的每个顶点,取X中M-不饱和的顶点x3,令S={x3},T=∅,则N(S)={y2,y3},N(S)⊃T, 取y3∈N(S)\T,y3为M-不饱和的,找到M-可扩路P=x3y3,令M=M△E(P)={ x1y2,x2y1,x3y3}
M没有饱和X的每个顶点,取X中M-不饱和的顶点x4,令S={x4},T=∅,则N(S)={y2,y3},N(S)⊃T, 取y2∈N(S)\T,y2为M-饱和的,且y2x1∈M(y2x1=x1y2),令S=S∪{x1}={x1, x4},T=T∪{y2}={y2}。
N(S)={ y2,y3}, N(S)⊃T, 取y3∈N(S)\T,y3为M-饱和的,且y3x3∈M(y3x3=x3y3),令S=S∪{x3}={x1, x4,x3},T=T∪{y3}={y2,y3}。
N(S)={y2,y3}=T={y2,y3},结束,没有完美匹配。
注意:手写时可以直接取x1,x3,x4即可,可以看出他们都是连接的y2,y3,两个y没有办法匹配给3个x。
代码实现
通过例子可以看出,S并没有必要,只需要N(S)再原来基础上更新即可,为简化,代码没有加入S。
变量及函数说明
int M[] 初始为 - 1, 下标为X下标,值为匹配的Y集合中的元素下标 ,做对称差时覆盖即可
bool X[Maxnum], Y[Maxnum] 初始为false, 用于判断X, Y集合中元素是否为M饱和的
vector<int> P 可扩路P,初始为空,记录X,Y集合中在P上的元素的下标
set<Vartype> NS, T 对应算法N(S)与T
bool visitedx[Maxnum], visitedy[Maxnum] 每次深搜时标记是否已遍历。
void Init(Graph &G) 初始化函数,参数:图G,功能:初始化图G
void Print(Graph G) 打印图函数,参数:图G,功能:以矩阵形式打印图,可去除
void PrintP(Graph G) 打印路径函数,参数:图G,功能:打印路径P
void PrintM(Graph G) 打印匹配集合M函数,参数:图G,功能:打印匹配集合M
void Delta() 对称差函数,参数:无,功能:M与E(P)做对称差
void DFS(Graph G,bool x,int start) 深度遍历函数(递归形式)参数:图G,X点集,开始结点下标start 作用:深度遍历,找可扩路
/*
Project: Hungarian method Edmonds
Date: 2020/01/02
Author: Frank Yu
void Init(Graph &G) 初始化函数,参数:图G,功能:初始化图G
void Print(Graph G) 打印图函数,参数:图G,功能:以矩阵形式打印图,可去除
void PrintP(Graph G) 打印路径函数,参数:图G,功能:打印路径P
void PrintM(Graph G) 打印匹配集合M函数,参数:图G,功能:打印匹配集合M
void Delta() 对称差函数,参数:无,功能:M与E(P)做对称差
void DFS(Graph G,bool x,int start) 深度遍历函数(递归形式)参数:图G,X点集,开始结点下标start 作用:深度遍历,找可扩路
*/
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<string>
#include<set>
#include<list>
#include<queue>
#include<vector>
#include<map>
#include<stack>
#include<iterator>
#include<algorithm>
#include<iostream>
#define Vartype string //顶点类型
#define EdgeType int
#define Maxnum 100 //二部图点集最大数量
using namespace std;
//图的数据结构
typedef struct Graph
{
Vartype X[Maxnum];
Vartype Y[Maxnum];
EdgeType Edge[Maxnum][Maxnum];//边表
int xnum, ynum,edgenum;//顶点数
}Graph;
//M 下标为X下标,值为匹配的Y集合中的元素下标 初始-1
int M[Maxnum];
//M是否饱和X、Y 饱和为True,不饱和为False
bool X[Maxnum],Y[Maxnum];
//可扩路P
vector<int> P;
//邻接点集合与T集合
set<Vartype> NS, T;
//标记是否已遍历过
bool visitedx[Maxnum], visitedy[Maxnum];
//初始化函数
void Init(Graph &G)
{
memset(G.Edge, 0, sizeof(G.Edge));
cout << "请输入X、Y顶点集个数:" << endl;
cin >> G.xnum >> G.ynum;
Vartype temp;
cout << "请输入X顶点集顶点名称:" << endl;
for (int i = 0; i < G.xnum; i++)
{
cin >> temp;
G.X[i] = temp;
}
//for (int i = 0; i < G.xnum; i++) cout << G.X[i] << '\t' << endl;
cout << "请输入Y顶点集顶点名称:" << endl;
for (int i = 0; i < G.ynum; i++)
{
cin >> temp;
G.Y[i] = temp;
}
//for (int i = 0; i < G.xnum; i++) cout << G.X[i] << '\t' << endl;
cout << "请输入边数:" << endl;
cin >> G.edgenum;
cout << "请输入边,空格分隔(例如: x y):" << endl;
Vartype x, y;
for (int i = 0; i < G.edgenum; i++)
{
cin >> x >> y;
int p1 = -1,p2 = -1;
for (int j = 0; j < G.xnum; j++)
if (!x.compare(G.X[j])) { p1 = j; break; }
for (int k = 0; k < G.ynum; k++)
if (!y.compare(G.Y[k])) { p2 = k; break;}
//cout << p1 << " " << p2;
if (p1 != -1 && p2 != -1)
{
G.Edge[p1][p2] = 1;
}
else
{
cout << "未找到该边,请检查端点是否输入有误!" << endl;
break;
}
}
}
//打印图函数
void Print(Graph G)
{
cout << '\t';
for (int i = 0; i < G.ynum; i++) cout << G.Y[i] << '\t';
cout << endl;
for (int i = 0; i < G.xnum; i++)
{
cout << G.X[i] << '\t';
for (int j = 0; j < G.ynum; j++)cout << G.Edge[i][j]<<'\t';
cout << endl;
}
}
//输出可扩路
void PrintP(Graph G)
{
cout << "P:";
for (int i = 0; i < P.size(); i++)
{
if (i % 2 == 0)cout << G.X[P[i]];
else cout << G.Y[P[i]];
}
cout << endl;
}
//输出集合M
void PrintM(Graph G)
{
bool flag = false;
cout << "M:{";
for (int i = 0; i < G.xnum; i++)
{
if (M[i] != -1 && !flag) { cout << G.X[i] << G.Y[M[i]]; flag = true; }
else if (M[i]!=-1&&flag)cout << ","<< G.X[i] << G.Y[M[i]];
}
cout <<"}"<<endl;
}
//集合M与E(P)做对称差
void Delta()
{
vector<int>::iterator it;
for (it = P.begin(); it != P.end();it++)
{
int x = *it;
it++;
int y = *it;
X[x] = true;
Y[y] = true;
M[x] = y;
}
}
//深度遍历函数(递归形式)参数:图G,X点集开始结点下标start 作用:深度遍历
void DFS(Graph G,bool x,int start)
{
/*
cout << "DFS(";
if (x)cout << "x,";
else cout << "y,";
cout << start << ")" << endl;*/
//X顶点集
if (x)
{
P.push_back(start);
cout << "当前路:" << endl;
PrintP(G);
visitedx[start] = true;
for (int i = 0; i < G.ynum; i++) if (G.Edge[start][i] == 1)NS.insert(G.Y[i]);
if (NS.size() == T.size())
{
cout << "N(S)==T,没有完美匹配" << endl;
system("pause");
}
for (int i = 0; i < G.ynum; i++)
{
//取Y中M - 饱和顶点
if (G.Edge[start][i] == 1 && !visitedy[i] && Y[i])//是邻接点且未访问 M - 饱和顶点Y[i]
{
T.insert(G.Y[i]);
cout << "取Y中M - 饱和顶点" << G.Y[i] << endl;
DFS(G,false,i);//递归深度遍历结点集Y
}
//Y为M - 不饱和顶点 找到可扩路P 与M做对称差
if (G.Edge[start][i] == 1 && !visitedy[i] && !Y[i])
{
cout << G.Y[i]<< "为M - 不饱和顶点,找到可扩路" << endl;
P.push_back(i);
PrintP(G);
Delta();
PrintM(G);
//返回步骤一
for (int i = 0; i < G.xnum; i++)
{
memset(visitedx, false, sizeof(visitedx));
memset(visitedy, false, sizeof(visitedy));
P.clear();
NS.clear();
T.clear();
//取X中M - 不饱和顶点
if (!X[i])DFS(G, true, i);
}
cout << "找到完美匹配";
PrintM(G);
cout << endl;
system("pause");
}
}
P.pop_back();
cout << "返回上一层前的路径:" << endl;
PrintP(G);
return;//返回至上一层
}
else//Y顶点集
{
//cout << G.Y[start];
P.push_back(start);
cout << "当前路:" << endl;
PrintP(G);
visitedy[start] = true;
for (int j = 0; j < G.xnum; j++)
{
if (M[j]==start)//找到Y[start]X[j]属于M
{
cout << "存在"<<G.Y[start]<<G.X[j]<<"属于M" << endl;
DFS(G, true, j);//递归深度遍历结点集X
}
}
P.pop_back();
cout << "返回上一层前的路径:" << endl;
PrintP(G);
return ;//返回至上一层
}
}
//匈牙利算法
int Hungarian(Graph &G)
{
int i;
memset(M, -1, sizeof(M));
cout << "1.输入初始M 2.M从空集开始" << endl;
cout << "请选择:";
cin >> i;
if (1 == i)
{
int num;
cout << "请输入M中边的数量:" << endl;
cin >> num;
cout << "请输入边,空格分隔(例如: x y):" << endl;
Vartype x, y;
for (int i = 0; i < num; i++)
{
cin >> x >> y;
int p1 = -1, p2 = -1;
for (int j = 0; j < G.xnum; j++)
if (!x.compare(G.X[j])) { p1 = j; break; }
for (int k = 0; k < G.ynum; k++)
if (!y.compare(G.Y[k])) { p2 = k; break; }
if (p1 != -1 && p2 != -1)
{
M[p1] = p2;
X[p1] = true;
Y[p2] = true;
}
else
{
cout << "未找到该边,请检查端点是否输入有误!" << endl;
break;
}
}
}
PrintM(G);
//步骤1 判断M是否饱和所有X元素
for (int i = 0; i < G.xnum; i++)
{
memset(visitedx, false, sizeof(visitedx));
memset(visitedy, false, sizeof(visitedy));
P.clear();
NS.clear();
T.clear();
//取X中M - 不饱和顶点
if (!X[i])DFS(G, true, i);
}
cout << "找到完美匹配";
PrintM(G);
cout<< endl;
return 0;
}
//主函数
int main()
{
Graph G;
Init(G);
Print(G);
Hungarian(G);
return 0;
}
以上面两个例题做测试
测试数据1
5 5
x1 x2 x3 x4 x5
y1 y2 y3 y4 y5
10
x1 y2
x1 y4
x2 y1
x3 y1
x3 y2
x3 y3
x4 y2
x5 y2
x5 y4
x5 y5
1 2
x1 y2
x5 y4
测试结果1
测试数据2
5 5
x1 x2 x3 x4 x5
y1 y2 y3 y4 y5
12
x1 y2
x1 y3
x2 y1
x2 y2
x2 y4
x2 y5
x3 y2
x3 y3
x4 y2
x4 y3
x5 y3
x5 y5
2
测试结果
更多数据结构与算法实现:数据结构(严蔚敏版)与算法的实现(含全部代码)
有问题请下方评论,转载请注明出处,并附有原文链接,谢谢!如有侵权,请及时联系。