匈牙利算法是众多用于解决
线性任务分配问题的算法之一,是用来解决二分图最大匹配问题的经典算法,可以在多项式时间内解决问题,由美国数学家Harold Kuhn 于1955年提出。此算法之所以被称作匈牙利算法是因为算法很大一部分是基于以前匈牙利数学家Dénes Kőnig和Jenő Egerváry的工作之上创建起来的.
定义
未盖点:设Vi是图G的一个顶点,如果Vi 不与任意一条属于匹配M的边相关联,就称Vi 是一个未盖点。
交错路:设P是图G的一条路,如果P的任意两条相邻的边一定是一条属于M而另一条不属于M,就称P是一条交错路。
可增广路:两个端点都是未盖点的交错路叫做可增广路。
伪代码
bool 寻找从k出发的对应项出的可增广路
{
while (从邻接表中列举k能关联到顶点j)
{
if (j不在增广路上)
{
把j加入增广路;
if (j是未盖点 或者 从j的对应项出发有可增广路)
{
修改j的对应项为k;
则从k的对应项出有可增广路,返回true;
}
}
}
则从k的对应项出没有可增广路,返回false;
}
void 匈牙利hungary()
{
for i->1 to n
{
if (则从i的对应项出有可增广路)
匹配数++;
}
输出 匹配数;
}
另
个人认为,
匈牙利算法其实就是
Ford-Fulkerson:dfs增广路最大流算法的提取和简化。
观察Ford-Fulkerson最大流dfs增广路的过程,因为二分图的特殊性质,这条增广路其实就是匈牙利算法中要求的交错路。
并且我们知道是Ford-Fulkerson最大流的时间复杂度为O(E*Maxflow),而二分图匹配中最大流不超过V,所以时间复杂度是O(VE)。
C++实现
//入门题:USACO 4.2.2 The Perfect Stall(POJ 1274)
const int MAXV = 405; //N1+N2
struct MaximumMatchingOfBipartiteGraph{
int vn1, vn2;
vector adj[MAXV];
void init(int n1, int n2){ //二分图两点集点的个数
vn1 = n1;
vn2 = n2;
for (int i = 0; i <= n1+n2; i ++) adj[i].clear();
}
/** 一定要注意n1点标号1~n1,n2点要把标号转换为n1+1~n1+n2**/
void add_uedge(int u, int v){
adj[u].push_back(v); adj[v].push_back(u);
}
bool vis[MAXV];
int mat[MAXV]; //记录已匹配点的对应点
bool cross_path(int u){
for (int i = 0; i < (int)adj[u].size(); i ++){
int v = adj[u][i];
if (!vis[v]){
vis[v] = true;
if (mat[v] == 0 || cross_path(mat[v])){
mat[v] = u;
return true;
}
}
}
return false;
}
int hungary(){
mem(mat, 0);
int match_num = 0;
for (int i = 1; i <= vn1; i ++){
mem(vis, 0);
if (cross_path(i)){
match_num ++;
}
}
return match_num;
}
}match;
int main(){
//freopen("test.in", "r", stdin);
//freopen("test.out", "w", stdout);
int n, m;
while(scanf("%d %d", &n, &m) != EOF){
match.init(n, m);
for (int i = 1; i <= n; i ++){
int num;
scanf("%d", &num);
for (int j = 0; j < num; j ++){
int tmp;
scanf("%d", &tmp);
match.add_uedge(i, n+tmp);
/** 一定要注意n1点标号1~n1,n2点要把标号转换为n1+1~n1+n2**/
}
}
printf("%d
", match.hungary());
}
return 0;
}