===【无权图最大匹配】===
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 0x3f3f3f3f
typedef long long LL;
#define MAXN 510
#define MAXM 250100
struct 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 0x3f3f3f3f
struct 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;//不存在完美匹配,返回-1
if(~linker[i]) ans += g[linker[i]][i];
}
return -ans;
}
}km;