【二分图匹配模板】

来源:互联网 发布:中国制造 知乎 编辑:程序博客网 时间:2024/05/22 04:26

===【无权图最大匹配】===

 

1)一个二分图中的最大匹配数=这个图中的最小点覆盖数 

König定理是一个二分图中很重要的定理,它的意思是,一个二分图中的最大匹配数等于这个图中的最小点覆盖数。如果你还不知道什么是最小点覆盖,我也在这里说一下:假如选了一个点就相当于覆盖了以它为端点的所有边,你需要选择最少的点来覆盖所有的边。

 2)最小路径覆盖=|G|-最大匹配数

在一个N*N的有向图中,路径覆盖就是在图中找一些路经,使之覆盖了图中的所有顶点,且任何一个顶点有且只有一条路径与之关联;(如果把这些路径中的每条路径从它的起始点走到它的终点,那么恰好可以经过图中的每个顶点一次且仅一次);如果不考虑图中存在回路,那么每每条路径就是一个弱连通集.由上面可以得出: 

1.一个单独的顶点是一条路径; 
2.如果存在一路径p1,p2,......pk,其中p1 为起点,pk为终点,那么在覆盖图中,顶点p1,p2,......pk不再与其它的  顶点之间存在有向边. 最小路径覆盖就是找出最小的路径条数使之成为G的一个路径覆盖. 路径覆盖与二分图匹配的关系:最小路径覆盖=|G|-最大匹配数;

 

3)二分图最大独立集=顶点数-二分图最大匹配

独立集:图中任意两个顶点都不相连的顶点集合。

 

【匈牙利算法(邻接表)(n*m)】n表示顶点数,m表示边数

bool vis[MAXN];int lin[MAXN], n;vector<int> g[MAXN];//n是左边点数的数量,g[i]:i表示左边的点标号,g[i]里面的是右边的点标号bool dfs(int u){for(int i = 0; i < g[u].size(); i++){int v = g[u][i];if(!vis[v]){vis[v] = true;if(-1 == lin[v] || dfs(lin[v])){lin[v] = u;return true;}}}return false;}int hungary(){int ans = 0;memset(lin,-1,sizeof(lin));for(int i = 0; i < n; i++){memset(vis,0,sizeof(vis));if(dfs(i)) ans++;}return ans;}


 【Hopcroft-Carp算法(sqrt(n)*m)】

#define rep(i,a,n) for(int i = a; i < n; i++)#define repe(i,a,n) for(int i = a; i <= n; i++)#define per(i,n,a) for(int i = n; i >= a; i--)#define clc(a,b) memset(a,b,sizeof(a))#define INF 0x3f3f3f3ftypedef long long LL;#define MAXN 510#define MAXM 250100struct Edge{int next, v;Edge(int a = 0, int b = 0){next = a, v = b;}}edge[MAXM];int head[MAXN], tol, n, dis, dx[MAXN], dy[MAXN], mx[MAXN], my[MAXN];bool vis[MAXN];void add_edge(int u, int v){edge[tol] = Edge(head[u], v);head[u] = tol++;}bool bfs(){queue<int> q;dis = INF;clc(dx,-1);clc(dy,-1);rep(i,0,n){if(-1 == mx[i]){q.push(i);dx[i] = 0;}}while(!q.empty()){int u = q.front();q.pop();if(dx[u] > dis) break;for(int i = head[u]; ~i; i = edge[i].next){int v = edge[i].v;if(-1 == dy[v]){dy[v] = dx[u]+1;if(-1 == my[v]) dis = dy[v];else{dx[my[v]] = dy[v]+1;q.push(my[v]);}}}}return dis != INF;}bool dfs(int u){for(int i = head[u]; ~i; i = edge[i].next){int v = edge[i].v;if(!vis[v] && dx[u]+1 == dy[v]){vis[v] = true;if(-1 != my[v] && dy[v] == dis) continue;if(-1 == my[v] || dfs(my[v])){my[v] = u;mx[u] = v;return true;}}}return false;}int maxmatch(){int ans = 0;clc(mx,-1);clc(my,-1);while(bfs()){clc(vis,0);rep(i,0,n){if(-1 == mx[i] && dfs(i))ans++;}}return ans;}


 


 


 


 ===【有权图最佳完美匹配】===

【KM算法 矩阵(n^3)】

#define rep(i,a,n) for(int i = a; i < n; i++)#define repe(i,a,n) for(int i = a; i <= n; i++)#define clc(a,b) memset(a,b,sizeof(a))#define MAXN 110#define INF 0x3f3f3f3fstruct KM{struct NODE{int to,w;NODE(int a, int b){to = a, w = b;}};int nx,ny;//两边的点数int linker[MAXN],lx[MAXN],ly[MAXN];//y中各点匹配状态,x,y中的点标号int slack[MAXN];bool visx[MAXN],visy[MAXN];int g[MAXN][MAXN];//二分图描述,纵坐标为左边,横坐标为右边,这里下标从1开始的bool dfs(int x){visx[x] = true;repe(y,1,ny){if(visy[y]) continue;int tmp = lx[x]+ly[y]-g[x][y];if(!tmp){visy[y] = true;if(-1 == linker[y] || dfs(linker[y])){linker[y] = x;return true;}}else if(slack[y] > tmp)slack[y] = tmp;}return false;}int max_km(){clc(linker,-1);clc(ly,0);repe(i,1,nx){lx[i] = -INF;for(int j = 0; j < ny; j++)if(g[i][j] > lx[i]) lx[i] = g[i][j];}repe(x,1,nx){clc(slack,0x3f);while(1){clc(visx,0);clc(visy,0);if(dfs(x))break;int d = INF;repe(i,1,ny){if(!visy[i] && d > slack[i])d = slack[i];}repe(i,1,nx) if(visx[i]) lx[i] -= d;repe(i,1,ny){if(visy[i]) ly[i] += d;else slack[i] -= d;}}}//最后统计最大值,加符号就是最小值int ans = 0;repe(i,1,ny){if(INF == g[linker[i]][i]) return -1;//不存在完美匹配,返回-1if(~linker[i]) ans += g[linker[i]][i];}return -ans;}}km;


 

0 0