1. 程式人生 > >二分圖最大匹配與最小頂點覆蓋(教程系列)uva11419——我目前關於最大匹配最清晰的解釋。

二分圖最大匹配與最小頂點覆蓋(教程系列)uva11419——我目前關於最大匹配最清晰的解釋。

定義什麼的百度拉拉,我只說證明.

1.假設我們現在已經用匈牙利演算法求出了最大匹配,很明顯現在已經木有增廣路了(即未匹配->匹配->未匹配這些形式的路徑,圖裡是木有的,不過一定要從下面說的那種特殊點開始)

2.現在我們從右邊開始標記一些點沿著(未匹配->匹配->未匹配......->匹配)這種形式的路徑進行,(特別注意的是這個起點一定沒有和匹配邊相鄰接,為什麼?看下面。)

標記路徑上經過的所有點。注意起點是右邊的點,結束時一定在匹配邊結束。

標記完後已經木有從右邊的這種點開始的未匹配邊了!!!(然後我將證明所有左邊已經標記的點和右邊未標記的點的數目和,就是最小覆蓋也等於最大匹配數)

3.對右邊的點分類,標記了的點,和沒標記的點,可以發現沒標記的點都有和匹配邊相連,顯然。。。而且沒標記的點對應的匹配邊的左邊那個點也是沒標記的。顯然。。

對於匹配邊也闊以分為兩類和右邊的沒標記的點相鄰接,和右邊的有標記的部分點相鄰接=左邊標記了的點數目,匹配邊數這樣分類時是木有重疊的,所以上述點數目和是最大匹配。

4.然後邊又闊以分為兩類。兩個端點都是標記點,兩個端點都是未標記點,嗯很顯然等於上面描述的點數目,綜上最大匹配數=最小覆蓋。

5.最小覆蓋<最大匹配是不可能的。。。

6.上述證明一定是正確的,我已經過了uva上的題了,用上面的證明。。。。。。。。。。(關鍵!!)不過要注意的對於那些度為0的點要排除掉!!!!!!!!!!!!!!!!!!!!!

此題就是把行當成左邊點,列是右邊點,然後n行m列有點的話讓左邊n號點連右邊m點然後就是求一個最小覆蓋。。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
vector<int>x[1050], y[1050];
int visitx[1050], visity[1050], matchy[1050],matchx[1050];
int getx[1050], gety[1050];
int n, m,point;
bool dfs(int num)
{
	visitx[num] = 1;
	for (int i = 0; i < x[num].size(); i++)
	{
		int to = x[num][i];
		if (visity[to])continue;//說白了這一步你只能走未匹配邊如果visity[to]=1代表走的是匹配邊或者i正搜尋中你繼續走下去闊能有迴路
		visity[to] = 1;         //還有就算你visity[to]=1你走後然後後增廣你會發現那個y是和兩個x相連的。。。
		if (matchy[to] == -1 ||  dfs(matchy[to]))//在dfs外面我們是一個一個遍歷x的但是當開始dfs後要到x必須走匹配邊而且是從y走所以這不用visitx[i]!=1這個條件
		{
			matchy[to] = num; matchx[num] = to;
			return true;
		}
	}
	return false;
}
int getans(int nn,int mm)
{
	int ans = 0;
	for (int i = 1; i <= nn; i++)matchx[i] = -1;
	for (int i = 1; i <= mm; i++)matchy[i] = -1;
	for (int i = 1; i <= nn; i++)
	{
		for (int j = 1; j <= nn; j++)
			visitx[j] = 0;
		for (int j = 1; j <= mm; j++)
			visity[j] = 0;
		if (dfs(i))ans++;
	}
	return ans;
}
void mdfs(int num)
{
	gety[num] = 1; visity[num] = 1;
	for (int i = 0; i < y[num].size(); i++)
	{
		int to = y[num][i];
		if (visitx[to])continue;//加速需要
		getx[to] = 1; visitx[to] = 1;
		mdfs(matchx[to]);
	}
}
void mark(int nn, int mm)//每個點最多遍歷一次所以是o(n)的
{
	for (int i = 1; i <= nn; i++)
		getx[i] = 0, visitx[i] = 0;
	for (int i = 1; i <= mm; i++)
		gety[i] = 0, visity[i] = 0;
	for (int i = 1; i <= mm; i++)
	{
		if (visity[i]||y[i].size()==0)continue;
		if (matchy[i]==-1)
		mdfs(i);
	}
}
int main()
{
	int k = 1;
	while (scanf("%d%d%d", &n, &m, &point)&&n&&m&&point)
	{
		for (int i = 1; i <= n; i++)x[i].clear();
		for (int i = 1; i <= m; i++)y[i].clear();
		for (int i = 0; i < point; i++)
		{
			int a, b;
			scanf("%d%d", &a, &b);
			x[a].push_back(b), y[b].push_back(a);
		}
		int ans = getans(n,m);
		mark(n,m);
		printf("%d", ans);
		for (int i = 1; i <= n; i++)
		{
			if (getx[i] == 1)
				printf(" r%d", i);
		}
		for (int i = 1; i <= m; i++)
		{
			if (gety[i] == 0&&y[i].size()!=0)
				printf(" c%d", i);
		}
		printf("\n");
	}
	return 0;
}