一: 二分图的最小覆盖 = 最大匹配数
证明:
最小覆盖即选择尽量少的点,使得每条边至少有一个端点被选中;
最大匹配数即两两没有公共点的边集达到最大;
<1. 假设最大匹配数>最小点覆盖,则存在另一条匹配边,使着大于最小点覆盖,但是这样就不符合
最小覆盖的定义,矛盾,假设不成立;
<2. 假设最大匹配数<最小点覆盖,因为最小点覆盖就是要求每条边至少有一个端点被选中,所以
最大匹配时被选的那些边,都至少要有一个端点被选中;
题目链接....................................................................
AC代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <vector>
#include <cmath>
using namespace std;
typedef long long ll;
const int MAX_V = 2200;
int V;
vector<int>G[MAX_V];
int match[MAX_V];
bool used[MAX_V],vis[MAX_V];
void add_edge(int u,int v)
{
G[u].push_back(v);
G[v].push_back(u);
}
bool dfs(int v)
{
used[v] = true;
vis[v] = true;
for(int i = 0; i < G[v].size(); i++)
{
int u = G[v][i],w = match[u];
vis[u] = true;
if(w < 0 || !used[w] && dfs(w))
{
match[v] = u;
match[u] = v;
return true;
}
}
return false;
}
int solve()
{
int res = 0;
memset(match,-1,sizeof(match));
for(int v = 0; v < V; v++)
{
if(match[v] < 0)
{
memset(used,0,sizeof(used));
if(dfs(v)) res++;
}
}
return res;
}
int main()
{
int n,m,p,x,y;
while(~scanf("%d %d %d",&n,&m,&p) && (n | m | p))
{
for(int i = 0; i < n + m; i++) G[i].clear();
for(int i = 0; i < p; i++)
{
scanf("%d %d",&x,&y);
x--;
y--;
add_edge(x,n + y);
}
V = n + m;
printf("%d ",solve());
memset(vis,0,sizeof(vis));
for(int i = 0; i < n; i++)
{
if(match[i] == -1) dfs(i);
}
for(int i = 0; i < V; i++)
{
if(vis[i] && i >= n) printf("c%d ",i - n + 1);
if(!vis[i] && i < n) printf("r%d ",i + 1);
}
puts("");
}
return 0;
}