===【無權圖最大匹配】===
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;