题意:矩阵上有一些小行星,占据了一些格子,我们每次操作可以清理一列中的所有小行星,也可以清理一行中的所有小行星,问最少进行多少次操作可以清理掉所有的小行星。
分析:一个小行星,要么清理该行,要么该列。所以也就是每个小行星对应的行列中至少选择一样来清理。下面建图,如果我们把每行看成集合一中的点,每列看成集合二中的点,一个小行星看成是其对应行列的连线,那么也就是说不能存在某一条连线两边的点都没有被选中的情况。这恰好就是二分图最小点集覆盖的要求。
#include <stdio.h>
#define N 501
int grid[N][N] ; //矩阵的行列分别属于二分图的两个顶点集V1、V2,其中行x∈V1,列y∈V2,存储数据方式:可达矩阵
int flag[N] ;
int link[N] ; //记录V2中y值所匹配的x(V1中)
int dfs ( int const x , int const n ) //匈牙利算法
{
int y ;
for ( y = 1 ; y <= n ; y ++ )
{
if ( grid[x][y] && 0 == flag[y] )
{
flag[y] = 1 ;
if ( 0 == link[y] || dfs ( link[y] , n ) )
{
link[y] = x ;
return 1 ;
}
}
}
return 0 ;
}
void Input ( int const k )
{
int i ;
for ( i = 1 ; i <= k ; i ++ )
{
int x , y ;
scanf ("%d%d" , & x , & y ) ;
grid[x][y] = 1 ;
}
}
void Init_Flag ( )
{
int i ;
for ( i = 0 ; i < N ; i ++ )
{
flag[i] = 0 ;
}
}
int
main ( )
{
int n , k ;
scanf ("%d%d" , & n , & k ) ;
Input ( k ) ;
int count ;
count = 0 ;
int x ;
for ( x = 1 ; x <= n ; x ++ )
{
Init_Flag ( ) ;
if ( dfs ( x , n ) )
{
count ++ ;
}
}
printf ("%d\n" , count ) ;
return 0 ;
}
记住条性质: 二分图最大匹配的König定理 最小点覆盖数 = 最大匹配数
最小点集覆盖==最大匹配。在这里解释一下原因,首先,最小点集覆盖一定>=最大匹配,因为假设最大匹配为n,那么我们就得到了n条互不相邻的边,光覆盖这些边就要用到n个点。现在我们来思考为什么最小点击覆盖一定<=最大匹配。任何一种n个点的最小点击覆盖,一定可以转化成一个n的最大匹配。因为最小点集覆盖中的每个点都能找到至少一条只有一个端点在点集中的边(如果找不到则说明该点所有的边的另外一个端点都被覆盖,所以该点则没必要被覆盖,和它在最小点集覆盖中相矛盾),只要每个端点都选择一个这样的边,就必然能转化为一个匹配数与点集覆盖的点数相等的匹配方案。所以最大匹配至少为最小点集覆盖数,即最小点击覆盖一定<=最大匹配。综上,二者相等。
设G=(V,E)是一个无向图。如顶点集V可分割为两个互不相交的子集V1,V2之并,并且图中每条边依附的两个顶点都分属于这两个不同的子集。则称图G为二分图。二分图也可记为G=(V1,V2,E)。
给定一个二分图G,在G的一个子图M中,M的边集{E}中的任意两条边都不依附于同一个顶点,则称M是一个匹配。