//二分图有一个重要模型 – 最小点覆盖.
结论 : 最小点覆盖 = 二分图最大匹配数.
解释: 最小点覆盖指的是选择尽量少的点, 使得每条边至少有一个端点被选中. 那么在二分图匹配中很容易可以被证明就是该个二分图的最匹配数.
(把图画出来写一写就知道了.)
举几个例子 :
经典例题UVa – 11419
//题意: 在一个矩阵上某些位置上有一些目标, 每一次可以发射一颗子弹, 这颗子弹可以把任意一行或一列上的目标打完, 问最少需要几颗子弹, 并输出这些子弹发射的位置.
//思路 : 把每一行看做一个X结点, 每一列看做一个Y结点, 每个目标所在的位置对应的点有一条边, 这样, 子弹打掉所有的目标意味着每条边值得至少有一个结点被选中. 即可以从该点打出子弹可以清除该个目标, 这样最小点覆盖数就是答案, 跑一遍匈牙利就可以出答案了.
//但是麻烦的是还要输出打子弹的位置. 这个就比较难搞了. 白书上说的是什么匈牙利树, 不懂. 然后看了很多博客, 把代码仔细研究研究, 懂了. 大概就是从左边没有覆盖的点再找增广路, 并且这个未被覆盖的x点不管有无边, 我们最后都一定不会选它. 找到增广路后再从右边的那个点开始找, (此时找到的右边的那个点一定是匹配了的), 再找起增广路, 此时一定是还有的, 因为匹配了的, 这样我们我们可以发现右边的那个点连接着两个左边的点, 对应在图中肯定在该点处放子弹是最优的, 所以一直进行下去就可以找到所有最优的点, 然后输出就可以了.
AC Code
/** @Cain*/
const int maxn=1e3+5;
bool g[maxn][maxn];
bool visx[maxn],visy[maxn];
int linkx[maxn],linky[maxn];
int n,m;
bool dfs(int x)
{
visx[x] = true;
for(int i=1;i<=m;i++){
if(visy[i] || !g[x][i]) continue;
visy[i] = true;
if(linky[i] == -1 || dfs(linky[i])){
linky[i] = x;
linkx[x] = i;
return true;
}
}
return false;
}
void solve()
{
int k;
while(~scanf("%d%d%d",&n,&m,&k)){
if(n + m +k == 0) break;
Fill(g,false); Fill(linkx,-1); Fill(linky,-1);
for(int i=1;i<=k;i++){
int u,v; scanf("%d%d",&u,&v);
g[u][v] = true;
}
int res = 0;
for(int i=1;i<=n;i++){
Fill(visy,false);
if(dfs(i)) res++;
}
printf("%d",res);
/*这里就是checkx集合中的点。如果这个点被匹配了,先不管
然后就是check未匹配的点,不管有无其他点与其连接,这里都
显然不用放炮弹. 然后剩余的操作就是我说的那样*/
Fill(visx,false); Fill(visy,false);
for(int i=1;i<=n;i++){
if(linkx[i] == -1) dfs(i);
}
bool flag = false;
for(int i=1;i<=n;i++){
if(!visx[i]){ printf(" r%d",i); flag = true; }
}
if(flag) printf("\n");
flag = false;
for(int i=1;i<=n;i++){
if(visy[i]){ printf(" c%d",i); flag = true; }
}
if(flag) printf("\n");
}
}
福工OJ – 2573
//有了上面这道的基础, 这就是一道水题, 直接做.
AC Code
/** @Cain*/
const int maxn=1e2+5;
bool g[maxn][maxn],vis[maxn];
int link[maxn];
char a[maxn][maxn];
int n,m;
bool dfs(int x)
{
for(int i=1;i<=m;i++){
if(vis[i] || !g[x][i]) continue;
vis[i] = true;
if(link[i] == -1 || dfs(link[i])){
link[i] = x;
return true;
}
}
return false;
}
void solve()
{
while(~scanf("%d",&n) && n){
scanf("%d",&m);
Fill(g,false); Fill(link,-1);
for(int i=1;i<=n;i++) scanf("%s",a[i]+1);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
int u = a[i][j]-'0';
if(u == 1) g[i][j] = true;
}
}
int res = 0;
for(int i=1;i<=n;i++){
Fill(vis,false);
if(dfs(i)){
res++;
}
}
printf("%d\n",res);
}
}