二分图匹配相关算法

二分图匹配相关算法二分图匈牙利算法二分图匈牙利算法二分图匈牙利算法这里简单记录下二分图匹配的相关算法 供自己使用 如果各位游客看到觉得浪费时间 便请移步 文章全为个人模板记录匈牙利算法匈牙利算法研究的是二分图的最大匹配 是对给定的二分图求得最大的满足条件的匹配数 匹配对 其算法思想是利用 Berge 定理和 Hall 定理将初始匹配通过迭代寻找增广路径得到最大匹配 每次迭代得到的匹配大小加 1 具体迭代实现有 DFS

二 分 图 匈 牙 利 算 法 二分图匈牙利算法
这里简单记录下二分图匹配的相关算法,供自己使用。如果各位游客看到觉得浪费时间,便请移步,文章全为个人模板记录

  • 匈牙利算法
    匈牙利算法研究的是二分图的最大匹配,是对给定的二分图求得最大的满足条件的匹配数,匹配对。其算法思想是利用Berge定理和Hall定理将初始匹配通过迭代寻找增广路径得到最大匹配,每次迭代得到的匹配大小加1
    具体迭代实现有DFS和BFS两种。下面贴上DFS版的cpp写法
    时间复杂度O( n × m n \times m n×m)


//匈牙利算法 时间复杂度为O(n*m) #include "bits/sdtc++.h" #define MAX 1003 using namespace std; int n,m,e; int map[MAX][MAX]; int vis[MAX]; int nextLeft[MAX],nextRight[MAX];//nextLeft[]是左部点匹配到右边的点,同理nextRight int fa[MAX]; bool Find(int u) { 
    //是DFS迭代寻找增广路径,(还有个形象的说法就是腾地) for (int i = 1; i<= m; i++) { 
    if (map[u][i] && !vis[i]) { 
    vis[i] = 1; if (-1==nextRight[i] || Find(nextRight[i])) { 
    //“腾地”,如果,nextRight[i]还未匹配右边的点,或者nextRight[i]还有增广路径可走有其他点可以与其匹配就“腾地” nextRight[i] = u; nextLeft[u] = i; return true; } } } return false; } int MaxMatch() { 
    //最大匹配,在主函数中直接调用MaxMatch(),返回最大匹配数。 int ans(0); memset(nextLeft,-1,sizeof(nextLeft)); memset(nextRight,-1,sizeof(nextRight)); for (int i = 1; i<=n; i++) { 
    if (-1 == nextLeft[i]) { 
    // { 
    memset(vis,0,sizeof(vis)); if (Find(i)) ans++; } } return ans; } int main(int argc,char *argv[]) { 
    scanf("%d%d%d", &n,&m,&e); for (int i = 1; i <= e; i++) { 
    int u,v; scanf("%d%d", &u,&v); if (u>=1&&u<=n&&v>=1&&v<=m) { 
    map[u][v] = 1; } } printf("%d\n", MaxMatch()); return 0; } 

H o p c r o f t − K a r p 算 法 Hopcroft-Karp 算法 HopcroftKarp
该算法也是求二分图最大匹配的,不过相对与匈牙利算法而言,效率更高,时间复杂度更优秀,但缺点是书写麻烦。

  • Hopcroft-Karp 算法
    HK算法相比匈牙利算法高效的地方在于寻找增广路上,匈牙利算法是每次迭代的时候寻找一条增广路,而HK算法是先寻找一个增广路集合,在一次搜索中寻找多条不相交的增广路径,形成极大增广路径集。
    HK算法的复杂度 O( n 1 2 × m n^{\frac{1}{2}}\times m n21×m)
    主函数直接调用MaxMatch().


//Hopcroft-Karp 算法 时间复杂度为O(n^(1/2)*m) //该算法是对匈牙利算法的优化,利用匈牙利算法一次只能找到一条增广路径, //Hopcroft-Karp就提出一次找到多条不相交的增广路径(不相交就是没有公共点和公共边的增广路径),称为增广路集 //然后根据这些增广路径添加多个匹配,并逐渐增加增广路径集中增广路径的长度。 #include "bits/stdc++.h" #define MAX 3003 #define INF 0x3f3f3f3f using namespace std; int nextLeft[MAX],nextRight[MAX]; //nextLeft[MAX]是左集合匹配右集合的点,同理nextRight也是 int dLeft[MAX],dRight[MAX]; //dLeft[MAX],dRight[MAX]是增广路径长度,或者说BFS深度 int dx[MAX],dy[MAX]; int map[MAX][MAX]; //map[MAX][MAX]存图 int nx,ny; //nx,ny分别是左集合点个数,右集合点个数 int dis; int vis[MAX]; //标记数组. bool searchPath() { 
    //寻找增广路径集,其增广路径集中每条增广路径长度一样 queue<int>Q; dis = INF; memset(dLeft,-1,sizeof(dLeft)); memset(dRight,-1,sizeof(dRight)); for (int i = 1;i <= nx; i++) { 
    //在BFS中宽度搜索 if (-1 == nextLeft[i]) { 
    //将未遍历的节点 入队 并初始化次节点距离为0  Q.push(i); dLeft[i] = 0; } } while (!Q.empty()) { 
    //广度搜索增广路径 int u = Q.front(); Q.pop(); if (dLeft[u] > dis) break; for (int v = 1; v <= ny; v++) { 
    //取右侧节点  if (map[u][v] && -1==dRight[v]) { 
    //右侧节点的增广路径的距离 dRight[v] = dLeft[u]+1; //v对应的距离 为u对应距离加1 if (-1 == nextRight[v]) dis = dRight[v]; else { 
    dLeft[nextRight[v]] = dRight[v]+1; Q.push(nextRight[v]); } } } } return dis != INF; } bool Find(int u) { 
    //Find函数,对增广路径集进行增广。 for (int v = 1; v <= ny; v++) { 
    if (!vis[v] && map[u][v] && dRight[v] == dLeft[u]+1) { 
    vis[v] = 1; if (nextRight[v] != -1 && dRight[v] == dis) continue; if (-1 == nextRight[v] || Find(nextRight[v])) { 
    nextRight[v] = u;nextLeft[u] = v; return true; } } } return false; } int MaxMatch() { 
    int ans(0); memset(nextRight,-1,sizeof(nextRight)); memset(nextLeft,-1,sizeof(nextLeft)); while(searchPath()) { 
    //不断进行增广路径集的操作,其增广路径也不断增长。 memset(vis,0,sizeof(vis)); for (int i = 1; i <= nx; i++) { 
    if (-1 == nextLeft[i]) { 
    if (Find(i)) ans++; } } } return ans; } int main(int argc,char *argv[]) { 
    // int n,m,e; int e; scanf("%d%d%d", &nx,&ny,&e); for (int i = 1; i <= e; i++) { 
    int u,v; scanf("%d%d", &u,&v); if (u>=1&&u<=nx&&v>=1&&v<=ny) { 
    map[u][v] = 1; } } printf("%d\n", MaxMatch()); return 0; } 

二 分 图 多 重 匹 配 二分图多重匹配

  • 多重匹配算法
    二分图普通的匹配是一对一的匹配,左集合X和右集合Y是一一对应的关系,如果允许Y集合中的一个元素和集合X中的多个元素匹配(通常有一个最大限制n),但是X集合中的一个元素只能和Y集合的一个元素匹配,这就是二分图多重匹配的模型
  • 算法步骤
    在构造二分图的多重匹配模型时,由于集合Y中的一个元素可以同时匹配集合X中的多个元素。所以,匈牙利算法中的cy[MAX]应该更换为一个二维数组 cy[MAX][MAX]。cy[i][j],表示与元素 y i y_i yi匹配的第j个元素,同时用vcy[i]来记录与元素 y i y_i yi匹配的元素的数量。
    二分查找可以构成多重匹配的limit值。在主函数main()方法中进行二分查找,并调用MulMatch()就可以得到limit值。

/*二分图多重匹配算法*/ #include "bits/stdc++.h" #define MAX 1001 int bmap[MAX][MAX]; int vis[MAX]; int nx,ny; int vcy[MAX]; int cy[MAX][MAX]; int limit; int left;int right; bool Find(int u) { 
    for (int i = 1; i <= ny; i++) { 
    if (bmap[u][i] && !vis[i]) { 
    vis[u] = 1; if (vcy[i] < limit) { 
    cy[i][vcy[i]++] = u; return true; } for (int j = 1; j <= vcy[i]; j++) { 
    if (Find(cy[i][j])) { 
    cy[i][j] = u; return true; } } } } return false; } bool MulMatch() { 
    memset(vcy,0,sizeof(vcy)); for (int i = 1; i <= nx; i++) { 
    memset(vis,0,sizeof(vis)); if (!Find(i)) return false; } return true; } using namespace std; int main(int argc,char *argv[]) { 
    /* 根据题意建图... */ left = 0, right = nx; //二分查找 while (left < right) { 
    limit = (left+right)/2; if (MulMatch()) right = limit; else left = limit+1; } /* 根据题意输出... */ return 0; } 

二 分 图 最 佳 匹 配 二分图最佳匹配

  • Kuhn-Munkres算法
/*二分图最佳匹配算法*/ /*Kuhn-Munkres算法*/ /*hdu 2255 例题*/ #include "bits/stdc++.h" #define MAX 1001 #define INF 0x3f3f3f3f using namespace std; int bmap[MAX][MAX]; int visx[MAX],visy[MAX],cx[MAX],cy[MAX]; int nextLeft[MAX],nextRight[MAX]; int w[MAX][MAX]; int n,m,ans; //n左m右 bool Find(int x) { 
    visx[x] = 1; for (int i = 1; i <= m; i++) { 
    if (!visy[i] && (cx[x]+cy[i] == w[x][i])) { 
    visy[i] = 1; if (!nextRight[i] || Find(nextRight[i])) { 
    nextRight[i] = x; nextLeft[x] = i; return true; } } } return false; } int kuhnMunkres() { 
    for (int i = 1; i <= n; i++) { 
    while (1) { 
    int D = INF; memset(visx,0,sizeof(visx)); memset(visy,0,sizeof(visy)); if (Find(i)) break; for (int j = 1; j <= n; j++) { 
    if (visx[j]) for (int k = 1; k <= m; k++) if (!visy[k]) D = min(D,cx[j]+cy[k]-w[j][k]); } if (D == INF) return -1; for (int j = 1; j <= n; j++) if (visx[j]) cx[j] -= D; for (int j = 1; j <= m; j++) if (visy[j]) cy[j] += D; } } ans = 0; for (int i = 1; i <= m; i++) ans += w[nextRight[i]][i]; return ans; } int main(int argc, char const *argv[]) { 
    while (scanf("%d", &n)!=EOF) { 
    m = n; memset(cy,0,sizeof(cy)); memset(cx,0,sizeof(cx)); memset(w,0,sizeof(w)); for (int i = 1; i <= n; i++) { 
    int D = 0; for (int j = 1; j <= m; j++) { 
    scanf("%d", &w[i][j]); D = max(D,w[i][j]); } cx[i] = D; } memset(nextLeft,0,sizeof(nextLeft)); memset(nextRight,0,sizeof(nextRight)); int ANS = kuhnMunkres(); printf("%d\n", ANS); } return 0; } 
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/231410.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 解决新版chrome跨域问题:cookie丢失以及samesite属性问题「建议收藏」

    解决新版chrome跨域问题:cookie丢失以及samesite属性问题「建议收藏」最近在使用前后端分离开发的时候,遇到了一个诡异的问题,无论如何设置跨域,同一个页面获取到的session始终不一致。发现问题:登录界面前后端分离,ajax提交登录时出错验证码接口和登录接口的session不一致(跨域问题)在网上搜索跨域问题,重新设置,问题依旧错因排除:ajax允许cookie(已经设置xhrFields:{withCredentials:true})springboot尝试设置了多种跨域方法(springboot解决跨域)深入分析:使用其它浏览器(fi

    2022年6月9日
    237
  • Ubuntu 登陆无限闪退

    Ubuntu 登陆无限闪退引子配置环境变量时,logout后,再次登陆,出现无限闪退情况,即输入密码,回车后闪了一下,又回到登陆界面,无奈欲重装虚拟机,觉太麻烦,故Google之。总结思路如下

    2022年7月21日
    19
  • Query.uniqueResult()计算数据总条数

    Query.uniqueResult()计算数据总条数1.如果是SQLQuery.uniqueResult(),返回的结果是BIgDecimal类型的总条数。2.如果是Query.unqueResult(),返回的结果是Long类型的总条数

    2022年9月30日
    4
  • 将JSON数组转化为List集合[通俗易懂]

    将JSON数组转化为List集合[通俗易懂]假如我们向redis中存放了一个JSON数组,从中获取的时候需要将JSON数组转化为List集合,然后将List对象返回给前端。1.引入hutool和fastjson依赖<!–hutool–><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId&gt

    2022年6月29日
    131
  • Recyclerview 刷新「建议收藏」

    Recyclerview 刷新「建议收藏」前言:recyclerview比起listview功能上更加丰富外(如横向列表),在Item复用上也更加灵活,比如listview的某个Item数据需要更新,要通过notifyDataSetChanged方法对全部Item进行刷新,而recyclerview则可以精准刷新。介绍:(1)notifyItemChanged(position)只刷新该position的Item,即只是该Item调用onBindViewHolder,因此如果对数据源进行插、移除操作不能改方法只刷新操作的Item,毕竟该

    2025年5月31日
    3
  • 用户行为路径分析报告_spark用户行为分析

    用户行为路径分析报告_spark用户行为分析请看题:已知用户行为表tracking_log,大概字段有:(user_id用户编号,op_id操作编号,op_time操作时间)要求:统计每天符合以下条件的用户数:A操作之后是B操作,AB操作必须相邻。生成数据,可以在sqlfiddle中测试:createtabletracking_log(idintprimarykeyAUTO_INCREMENT,user_idintnotnull,op_idchar(4)notnull,op

    2022年8月24日
    9

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号