二分图 模板
Posted zhgyki
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二分图 模板相关的知识,希望对你有一定的参考价值。
二分图如果是没有权值的,求最大匹配。则是用匈牙利算法求最大匹配。如果带了权值,求最大或者最小权匹配,则必须用KM算法。
其实最大和最小权匹配都是一样的问题。只要会求最大匹配,如果要求最小权匹配,则将权值取相反数,再把结果取相反数,那么最小权匹配就求出来了。
下面是几个模板:
1、匈牙利算法,例子是hdu2063
时间复杂度:O(VE)
#include<iostream> #include<string.h> #include<algorithm> #include<stdio.h> #include<queue> using namespace std; typedef long long ll; typedef unsigned long long ull; typedef long long ll; typedef pair<int,int> PII; #define mod 1000000007 #define pb push_back #define mp make_pair #define all(x) (x).begin(),(x).end() #define fi first #define se second //head #define MAX 505 int line[MAX][MAX],used[MAX],nxt[MAX]; int k,n,m; bool Find(int x) { for(int i=1;i<=m;i++) { if(line[x][i]&&!used[i]) { used[i]=1; if(nxt[i]==0||Find(nxt[i])){ nxt[i]=x; return true; } } } return false; } int match() { int sum=0; for(int i=1;i<=n;i++) { memset(used,0,sizeof(used)); if(Find(i)) sum++; } return sum; } int main() { ios_base::sync_with_stdio(false); cin.tie(0); while(cin>>k) { if(k==0) break; memset(line,0,sizeof(line)); memset(nxt,0,sizeof(nxt)); cin>>n>>m; while(k--) { int u,v; cin>>u>>v; line[u][v]=1; } cout<<match()<<endl; } return 0; }
2、Hopcroft-Karp算法
时间复杂大为 O(V^0.5 E)
这个算法比匈牙利算法的时间复杂度要小,大数据可以采用这个算法
#include <stdio.h> #include <algorithm> #include <iostream> #include <string.h> #include <vector> #include <queue> using namespace std; /* ******************************* * 二分图匹配(Hopcroft-Carp算法) * 复杂度O(sqrt(n)*E) * 邻接表存图,vector实现 * vector先初始化,然后假如边 * uN 为左端的顶点数,使用前赋值(点编号0开始) */ const int MAXN = 505; const int INF = 0x3f3f3f3f; vector<int>G[MAXN]; int uN; int Mx[MAXN],My[MAXN]; int dx[MAXN],dy[MAXN]; int dis; bool used[MAXN]; bool SearchP() { queue<int>Q; dis = INF; memset(dx,-1,sizeof(dx)); memset(dy,-1,sizeof(dy)); for(int i = 0 ; i < uN; i++) if(Mx[i] == -1) { Q.push(i); dx[i] = 0; } while(!Q.empty()) { int u = Q.front(); Q.pop(); if(dx[u] > dis)break; int sz = G[u].size(); for(int i = 0;i < sz;i++) { int v = G[u][i]; if(dy[v] == -1) { dy[v] = dx[u] + 1; if(My[v] == -1)dis = dy[v]; else { dx[My[v]] = dy[v] + 1; Q.push(My[v]); } } } } return dis != INF; } bool DFS(int u) { int sz = G[u].size(); for(int i = 0;i < sz;i++) { int v = G[u][i]; if(!used[v] && dy[v] == dx[u] + 1) { used[v] = true; if(My[v] != -1 && dy[v] == dis)continue; if(My[v] == -1 || DFS(My[v])) { My[v] = u; Mx[u] = v; return true; } } } return false; } int MaxMatch() { int res = 0; memset(Mx,-1,sizeof(Mx)); memset(My,-1,sizeof(My)); while(SearchP()) { memset(used,false,sizeof(used)); for(int i = 0;i < uN;i++) if(Mx[i] == -1 && DFS(i)) res++; } return res; } int main() { int k,n,m; while(~scanf("%d",&k)) { if(k==0) break; scanf("%d %d",&n,&m); for(int i=0;i<MAXN;i++) G[i].clear(); while(k--) { int u,v; scanf("%d %d",&u,&v); u--; v--; G[u].push_back(v); //G[v].push_back(u); } uN=n; printf("%d ",MaxMatch()); } return 0; }
3.Kuhn-Munkras算法
时间复杂度:O(m*m*n)
#include <stdio.h> #include <algorithm> #include <string.h> #include <iostream> using namespace std; /* KM算法 * 复杂度O(nx*nx*ny) * 求最大权匹配 * 若求最小权匹配,可将权值取相反数,结果取相反数 * 点的编号从0开始 */ const int N = 310; const int INF = 0x3f3f3f3f; int nx,ny;//两边的点数 int g[N][N];//二分图描述 int linker[N],lx[N],ly[N];//y中各点匹配状态,x,y中的点标号 int slack[N]; bool visx[N],visy[N]; bool DFS(int x) { visx[x] = true; for(int y = 0; y < ny; y++) { if(visy[y])continue; int tmp = lx[x] + ly[y] - g[x][y]; if(tmp == 0) { visy[y] = true; if(linker[y] == -1 || DFS(linker[y])) { linker[y] = x; return true; } } else if(slack[y] > tmp) slack[y] = tmp; } return false; } int KM() { memset(linker,-1,sizeof(linker)); memset(ly,0,sizeof(ly)); for(int i = 0;i < nx;i++) { lx[i] = -INF; for(int j = 0;j < ny;j++) if(g[i][j] > lx[i]) lx[i] = g[i][j]; } for(int x = 0;x < nx;x++) { for(int i = 0;i < ny;i++) slack[i] = INF; while(true) { memset(visx,false,sizeof(visx)); memset(visy,false,sizeof(visy)); if(DFS(x))break; int d = INF; for(int i = 0;i < ny;i++) if(!visy[i] && d > slack[i]) d = slack[i]; for(int i = 0;i < nx;i++) if(visx[i]) lx[i] -= d; for(int i = 0;i < ny;i++) { if(visy[i])ly[i] += d; else slack[i] -= d; } } } int res = 0; for(int i = 0;i < ny;i++) if(linker[i] != -1) res += g[linker[i]][i]; return res; }
参考博客:https://www.cnblogs.com/kuangbin/archive/2012/08/26/2657446.html
以上是关于二分图 模板的主要内容,如果未能解决你的问题,请参考以下文章