了解这个算法之前 首先了解一个概念 :增广路

增广路 :简单的说 ,是二分图匹配中的一条边,他总是从 左边集合的一个点出发通过一条没有被匹配的边连接到右边集合,再从该点通过一条 匹配过的边连接到右边集合。

图1为 正常的二分图

图2为二分图的最大匹配

图3 为二分图最大匹配中的增广路 两条 紫色一条 黄色一条。


了解这个算法之前,还需要对一个算法进行比较: 匈牙利算法

匈牙利算法 如果用增广路来说明的话,就是从一个点出发,如果找到一个匹配 就连接这个匹配。

如果当前这个点 被匹配过的话,就去寻找一个增广路,找到左边集合中的点,看该点是否还能匹配到 右边集合的点 匹配到最后 增广路的长度为 2*n,最终无法匹配。

简单的说就是,每次判断能匹配就匹配,匹配不到就去寻找一个增广路使他继续匹配。

这样的复杂度 O(N*M)

如果数据太大,会爆TLE,所以HK算法将其复杂度优化 ->  O(sqrt(n)*m)


再者:了解HK算法的本质:是对匈牙利的算法的优化,匈牙利算法一次只更新一条增广路,而HK算法,同时更新多条互不相交的最短增广路。

经证明 :当更新到sqrt(n)的时候,最大匹配已被全部OK。复杂度证明 自行搜索。

然后开始正经的 算法讲解(一定要了解本质是对匈牙利算法的优化):

所以根据 优化这条性质出手

第一步:他需要找到当前最短的匹配 找不到就去找最短的增广路

左边第一个点 链接到 右边第一个点 发现右边第一个点未匹配 ,那么就连起来 更新当前最短的路程

如果发现右边的点匹配过,那么就去寻找 一条增广路,用BFS实现。

比如上图中:第一次 左1连接右3,左2连接右2,左4连接右4 这是长度为1的匹配.

第二次更新:左边只有第三个点还未进行匹配,所以用第三个点更新,左3连右2,右2已经被标记过了,那么找到右2的归属者:左2,对左2进行 搜索发现可以去连接到右3,所以当前的匹配长度为3,所以继续寻找匹配,用匈牙利算法递归进行匹配即可。

这一部分的代码总结为:

/****
前期数组定义(一遍):
nR :标记右边集合的点是否被标记
nL :标记左边集合的点是否被标记
dR : 更新当前增广路的距离,也便于更新
dL :同上
****/
bool judge()// 更新当前最短的增广路
{memset(dR,-1,sizeof(dR));//初始化的意思是 :因为多条增广路互不相交,所以一旦发生变化,另一个就不能再去标记了。memset(dL,-1,sizeof(dL));dis=INF;queue<int>q;for(int i=1;i<=n;i++)   {if(nL[i]==-1){q.push(i);dL[i]=0;}}while(!q.empty()){int u=q.front();q.pop();if(dL[u]>dis) break;//保证最短长度的赠广路for(int i=1;i<=m;i++){if(mp[u][i]<=t&&dR[i]==-1)//dR[i]!=-1 保证所有增广路都不相交{dR[i]=dL[u]+1;if(nR[i]==-1) dis=dR[i]; //如果当前点没有被标记 ,那么可以匹配到else//否则找到他的归属者,用归属者进入队列 ,寻找增广路{dL[nR[i]]=dR[i]+1;q.push(nR[i]);}}}}return dis!=INF;//只要能找到增广路 dis就不为INF
}

第二步:我们匹配完多条增广路之后,我们就用匈牙利的思想就去更新,这多条增广路,也是遍历左边集合中没有匹配到的点,看他们是否可以通过一条增广路匹配到,但会出现一个问题: 会增加时间,例如:

如果一个点 已经被匹配到 ,并且他是最后一个匹配的节点,那么这个节点连过去之后一定不可能成为增广路。所以不需要判断否则会耽误很多时间:

bool Find(ll x)
{for(int i=1;i<=m;i++){if(!vis[i]&&mp[x][i]<=t&&dL[x]+1==dR[i]){vis[i]=1;if(nR[i]!=-1&&dR[i]==dis)  continue; //特判一下if(nR[i]==-1||Find(nR[i]))//找到就匹配,找不到就匹配他的父辈节点{nR[i]=x;nL[x]=i;return true;}}}return false;
}

第三步:进行最大匹配就可以了。结束条件是 找不到增广路:

ll MaxMatch()
{ll cnt=0;while(judge()){for(int i=1;i<=n;i++){for(int k=1;k<=m;k++) vis[k]=false;if(nL[i]==-1)if(Find(i)) cnt++;}}return cnt;
}

最后附加一个例题:

Rain on your Parade  POJ的一个题:可以去搜搜

AC:

#include<bits/stdc++.h>
#define ll long long
#include <stdio.h>
#include <algorithm>
#pragma GCC optimize(2)
using namespace std;
const int maxn=1e6+1000;
const ll INF=10000000000;
inline ll read()
{int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;
}ll  n,m;
ll dis;
double t;
bool vis[5000];
int nR[5000],nL[5000];
int dR[5000],dL[5000];
struct node{double x,y;double v;
}gest[5000],umb[5000];
double cal(node a,node b)
{return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
bool judge()// 更新当前最短的增广路
{memset(dR,-1,sizeof(dR));memset(dL,-1,sizeof(dL));dis=INF;queue<int>q;for(int i=1;i<=n;i++)   {if(nL[i]==-1){q.push(i);dL[i]=0;}}while(!q.empty()){int u=q.front();q.pop();if(dL[u]>dis) break;//保证最短长度的赠广路for(int i=1;i<=m;i++){if((cal(umb[i],gest[u])/gest[u].v)<=t&&dR[i]==-1)//dR[i]!=-1 保证所有增广路都不相交{dR[i]=dL[u]+1;if(nR[i]==-1) dis=dR[i];else{dL[nR[i]]=dR[i]+1;q.push(nR[i]);}}}}return dis!=INF;
}
bool Find(ll x)
{for(int i=1;i<=m;i++){if(!vis[i]&&(cal(umb[i],gest[x])/gest[x].v)<=t&&dL[x]+1==dR[i]){vis[i]=1;if(nR[i]!=-1&&dR[i]==dis)  continue;if(nR[i]==-1||Find(nR[i])){nR[i]=x;nL[x]=i;return true;}}}return false;
}
ll MaxMatch()
{ll cnt=0;while(judge()){for(int i=1;i<=n;i++){for(int k=1;k<=m;k++) vis[k]=false;if(nL[i]==-1)if(Find(i)) cnt++;}}return cnt;
}
void restart()
{memset(nR,-1,sizeof(nR));memset(nL,-1,sizeof(nL));
}
int main()
{int T;scanf("%d",&T);int cas=0;bool f=false;while(T--){restart();scanf("%lf",&t);scanf("%lld",&n);for(int i=1;i<=n;i++) scanf("%lf%lf%lf",&gest[i].x,&gest[i].y,&gest[i].v);scanf("%lld",&m);for(int i=1;i<=m;i++) scanf("%lf%lf",&umb[i].x,&umb[i].y);printf("Scenario #%d:\n%lld\n\n",++cas,MaxMatch());}return 0;
}

PS:还是注意 优化那个 如果当前节点被标记过并且是最后一个 匹配点,该条路就一定不能成为增广路!

二分图匹配 Hopcroft-Carp (HK) 算法详解 附例题相关推荐

  1. python直线拟合_RANSAC算法详解(附Python拟合直线模型代码)

    之前只是简单了解RANSAC模型,知道它是干什么的.然后今天有个课程设计的报告,上去讲了一下RANSAC,感觉这个东西也没那么复杂,所以今天就总结一些RASAC并用Python实现一下直线拟合. RA ...

  2. 数学规划详解(附例题及部分Python实现)

    数学规划详解(附例题及Python实现) 例题来自于清风老师的数学建模课,个人认为讲的非常好,欢迎大家购买 一.概述 1.1 定义 数学规划是运筹学的一个分支,在约束条件下,按照目标函数来寻求计划管理 ...

  3. 关键点匹配——商汤loFTR算法详解与论文解读

    论文地址:https://openaccess.thecvf.com/content/CVPR2021/papers/Sun_LoFTR_Detector-Free_Local_Feature_Mat ...

  4. DFS (深度优先搜索) 算法详解 + 模板 + 例题,这一篇就够了

    深度优先搜索算法(Depth First Search,简称DFS):一种用于遍历或搜索树或图的算法. 沿着树的深度遍历树的节点,尽可能深的搜索树的分支.当节点v的所在边都己被探寻过或者在搜寻时结点不 ...

  5. 数据结构-数组-字符串匹配:Knuth-Morris-Pratt算法(详解附完整代码)

    字符串匹配 字符串抽象数据类型 字符串模式匹配 简单的字符串匹配 Knuth-Morris-Pratt算法 背景分析 失配函数 定义 实现方法 函数分析 KMP函数 实现方法 函数分析 失配信息的另一 ...

  6. dijkstra算法详解加例题分析 NOIP 2012 文化之旅

    首先说一下什么叫单源最短路径问题: 给定一个带权有向图G=(V,E),其中每条边的权是一个实数.另外,还给定V中的一个顶点,称为源.现在要计算从源到其他所有各顶点的最短路径长度.这里的长度就是指路上各 ...

  7. 迪杰斯特拉算法详解+模版+例题

    迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家狄克斯特拉于1959 年提出的,因此又叫狄克斯特拉算法.是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题.迪杰斯特拉算法主要特 ...

  8. 并查集-算法详解及例题(最小生成树问题)

    一.并查集的概念: 并查集(Union-find Sets)是一种非常精巧而实用的数据结构,它主要用于处理一些不相交集合的合并问题.一些常见的用途有求连通子图.求最小生成树的 Kruskal 算法和求 ...

  9. 多目标跟踪(MOT)中的卡尔曼滤波(Kalman filter)和匈牙利(Hungarian)算法详解

    多目标跟踪(MOT)中的卡尔曼滤波(Kalman filter)和匈牙利(Hungarian)算法详解 1. 概览 在开始具体讨论卡尔曼滤波和匈牙利算法之前,首先我们来看一下基于检测的目标跟踪算法的大 ...

最新文章

  1. jQuery的选择器
  2. java wed登录面 代码_java web 登录界面
  3. diou diou_nms代码分享
  4. 快速搭建 Serverless 人脸识别离线服务
  5. 1、MySQL 8.0.20最新版本在Linux上安装
  6. vuejs构建的单页面应用history模式子页面微信分享在iOS中遇到的问题
  7. Google Chrome 总提示flash插件过期,用命令行模式解决
  8. linux 脚本 整数 赋值,shell基础!!熟悉编程规范与变量
  9. 18复变函数的积分(四)
  10. SpringMVC, Spring和Mybatis整合案例一
  11. thinkphp5.x之Collection(集合)解析 php集合
  12. MySql处理Unicode字符串
  13. HTML5七夕情人节表白网页制作【爱情树-Html5实现唯美表白动画代码】HTML+CSS+JavaScript浪漫告白 求婚必备
  14. 在微软工作有多舒服?
  15. FLUX-TMS-物流整体解决方案 附下载地址
  16. 量子计算机原理 纠缠,白话量子计算机原理【前面的那个有错误,重新理清了一下思路】...
  17. TypeWriter: Neural Type Prediction with Search-based Validation基于搜索的神经网络预测器
  18. python selenium学习之新浪微博
  19. “展厅三维全景”技术,将产品和企业文化以vr展示出来
  20. 华虹技通华为鸿蒙,浩丰科技(300419)个股分析_牛叉诊股_同花顺财经

热门文章

  1. 实习每日总结_20161214
  2. 配置Exchange Server 2010多种邮件客户端收发电子邮件
  3. 分享一些前端开发人员必备的工具,脚本和资源
  4. Docker神器之百度云下载(群辉 LEDE)
  5. Oracle账号怎么注销,qq账号如何永久注销?qq账号永久注销的条件以及详细操作方法...
  6. LaTex 在图片上添加文字和公式
  7. C Primer Plus第六版第五章运算符,表达式,语句源码
  8. 安卓GB28181设备语音广播和语音对讲(Android GB28181 语音广播和语音对讲)实现
  9. 做好软件测试的关键是什么,做好测试计划和测试用例的工作的关键是什么?
  10. JAVA数独解题(四):数对法