二分图匹配之HK算法(知识点总结)

思路来源

https://blog.csdn.net/gddswlz/article/details/9086207(O(sqrt(n)*m)复杂度证明)

https://blog.csdn.net/discreeter/article/details/51649155(板子来源)

https://blog.csdn.net/qq_35776579/article/details/54945092

心得

看不懂复杂度证明,会用就行了,匹配题建图是核心

如果有谁懂复杂度证明,还望不吝赐教

知识点总结

每次bfs寻找多条长为d的增广路,第一次搜索时d至少为1,第二次d至少为2,依次往上增,

每一次bfs分层之后,把所有为d的增广路通过dfs完成匹配

一次bfs+循环内对应所有dfs的复杂度是O(m)的,

那么由于玄学地不会bfs超过sqrt(n)次,所以复杂度是O(sqrt(n)*m)的

每次把所有没有被匹配的当做距离0多源bfs,对当前匹配的进行距离分层,

匈牙利算法每次对于一个点搜所有边只搜到一条路径,不管长短,所以是O(n*m);

而HK算法每次处理所有长度为d的增广路,将其用一次O(m)的复杂度匹配完

板子整理(以poj1469为例)

//hopcroft_karp算法,复杂度O(sqrt(n)*m)
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
const int N = 320;
const int INF = 0x3f3f3f3f;
struct edge
{int to, next;
}e[N*N];
int match[N], head[N];
bool used[N];
int p, n;
int nx, ny, cnt, dis; //nx,ny分别是左点集和右点集的点数
int dx[N], dy[N], cx[N], cy[N]; //dx,dy分别维护左点集和右点集的标号
//cx表示左点集中的点匹配的右点集中的点,cy正好相反
void add(int u,int v)
{e[cnt].to = v;e[cnt].next = head[u];head[u] = cnt++;
}
bool bfs() //寻找增广路径集,每次只寻找当前最短的增广路
{queue que;dis = INF;memset(dx, -1, sizeof dx);memset(dy, -1, sizeof dy);for(int i = 1; i <= nx; i++)//将未遍历的节点入队,并初始化次节点距离为0if(cx[i] == -1) {que.push(i);dx[i] = 0;}while(!que.empty()){int u = que.front();que.pop();if(dx[u] > dis) break;for(int i = head[u]; i != -1; i = e[i].next){int v = e[i].to;if(dy[v] == -1){dy[v] = dx[u] + 1;if(cy[v] == -1) dis = dy[v]; //找到了一条增广路,dis为增广路终点的标号else dx[cy[v]] = dy[v] + 1,que.push(cy[v]);}}}return dis != INF;
}
int dfs(int u)
{for(int i = head[u]; i != -1; i = e[i].next){int v = e[i].to;if(! used[v] && dy[v] == dx[u] + 1) //如果该点没有被遍历过并且距离为上一节点+1{used[v] = true;if(cy[v] != -1 && dy[v] == dis) continue; //u已被匹配且已到所有存在的增广路终点的标号,再递归寻找也必无增广路,直接跳过if(cy[v] == -1 || dfs(cy[v]) ){cy[v] = u,cx[u] = v;return 1;}}}return 0;
}
int hopcroft_karp()
{int res = 0;memset(cx, -1, sizeof cx);memset(cy, -1, sizeof cy);while(bfs()){memset(used, 0, sizeof used);for(int i = 1; i <= nx; i++)if(cx[i] == -1)res += dfs(i);}return res;
}
int main()
{int t, a, b;scanf("%d", &t);while(t--){cnt = 0;memset(head, -1, sizeof head);scanf("%d%d", &p, &n);for(int i = 1; i <= p; i++){scanf("%d", &a);for(int j = 0; j < a; j++){scanf("%d", &b);add(i, b);}}nx = p, ny = n;if(hopcroft_karp() == p) printf("YES\n");else printf("NO\n");}return 0;
}

 


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部