二分图的最大匹配——匈牙利算法

来源:互联网 发布:js qq客服代码 编辑:程序博客网 时间:2024/05/22 15:18

背景知识:
什么是二分图?
二分图就是一种特殊的图。这个图可以将整个集合分成了两个子集,满足所有弧都是从其中一子集的顶点射向另一个子集的顶点同一个子集中的点没有弧相连

二分图的最大匹配:
二分图的最大匹配就是使两个集合的点尽可能多一一对应

本次介绍的是匈牙利算法

匈牙利算法的原理就是寻找增广路径以扩大匹配数量。与最大流增广算法原理基本一致。

增广路径:
若P是图G中一条连通两个未匹配顶点的路径,并且属于M的边和不属于M的边(即已匹配和待匹配的边)在P上交替出现,则称P为相对于M的一条增广路径。(百度百科)

源代码较长的原因是1.读入优化2.边表存储。
源代码的输入格式为POJ 1469所描述。


源代码:

/*About: 二分图最大匹配_匈牙利算法 From: POJ 1469 Auther: kongse_qiDate:2017/04/22*///#include <bits/stdc++.h>/* POJ的编译器不支持该头文件... */#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <vector>#include <string.h> #define maxn 10005 using namespace std;struct Edge{    int from;    int to;    int weight;    Edge(){} //初始化    Edge(int f, int t, int w):        from(f), to(t), weight(w){}//读入};//边表int times; /* 输入次数(多组数据) */int n, m, ans; /* 子集A的顶点数与子集B的顶点数 */vector<Edge> edge;vector<int> arc[maxn]; /* 存储每个起点所有的边在edge里的编号 */typedef vector<int>::iterator iterator_t; /* vector的迭代器 */bool check[maxn]; /* 用于检查是否已经匹配 */int match[maxn]; /* 对应的匹配节点 */char *X, *Buffer, c; /* fread读入优化 */int input; /* 同上 */void Get_All() /* 读入优化,一次读入整个文件 */{    long long file_lenth;    fseek(stdin, 0, SEEK_END);    file_lenth = ftell(stdin);    rewind(stdin);    Buffer = (char*)malloc(1*file_lenth);    fread(Buffer,1, file_lenth, stdin);    X = Buffer;    return ;}void Get_Int() /* 将字符串转换为数字 */{    c = *X;    input = 0;    while(c < '0' || c > '9')   c = *++X;    while(c >= '0' && c <= '9')    {        input = input*10+c-'0';        c = *++X;    }    return ;}void Init() /* 由于是多组数据,需要将上一次的数据清空 */{    edge.resize(0);    for(unsigned i = 0; i != n; ++i)    {        arc[i].resize(0);    }    ans = 0;    return ;}void Read() /* 读入数据 */{    int cur, nums;    Get_Int(), n = input;    Get_Int(), m = input;    for(unsigned i = 0; i != n; ++i)    {        edge.reserve(edge.size());         Get_Int(), nums = input;        arc[i].resize(nums);        for(unsigned j = 0; j != nums; ++j)        {            Get_Int(), cur = input;            edge.push_back(Edge(i, cur+n, 0));             /*加n的原因是他的两集合的节点序号都是从1开始,需要分开 */            /* 如果用网络流做那么还需要将反向弧放入集合 */            arc[i][j] = edge.size()-1; /*编号 */        }    }    return ;}bool Dfs(int cur){    int cur_to;    for(iterator_t i = arc[cur].begin(); i != arc[cur].end(); ++i)    /* 遍历所有与cur节点相连的弧 */    {        cur_to = edge[*i].to; /* 该弧终点 */        if(check[cur_to] == false) /* 如果当前未遍历到 */        {            check[cur_to] = true; /* 那么此次遍历 */            if(match[cur_to] == -1 || Dfs(match[cur_to])) //如果当前节点为匹配或者它匹配的节点(递归)未匹配            {                match[cur] = cur_to; /* 匹配两节点 */                match[cur_to] = cur;                return true; /* 匹配成功,最大匹配数+1 */            }        }    }     return false; /* 匹配未成功 */}void Match(){    memset(match, -1, sizeof match);    for(unsigned i = 0; i != n; ++i) /* 所有节点均尝试匹配 */    {        if(match[i] == -1) /* 如果当前节点未被匹配 */        {            memset(check, 0, sizeof check); /* 将上一次匹配数据置空 */            if(Dfs(i)) /* 如果匹配成功 */            {                ++ans;            }        }    }     return ;}int main(){    //freopen("test.in", "r", stdin);    Get_All();    Get_Int(), times = input;    while(times --> 0) /* times -= 1直到times == 0 */    {        Init();        Read();        Match();        printf("%d\n", ans);    }    return 0;}

关于上文读入优化
关于上文的结构体初始化


自此结束。
箜瑟_qi 2017.04.22 15:47

0 1
原创粉丝点击