【二分圖匹配模板】

===【無權圖最大匹配】===

 

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;


 

發佈了95 篇原創文章 · 獲贊 3 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章