行为左边点集,列为右边点集,障碍为边,这样消灭所有障碍即每条边都连着一个点。这样选最少的点即可。这样问题就是求解最小点覆盖集,在二分图中等价于二分最大匹配。但是这里还要求出最小点覆盖集。方法是,在求出二分图最大匹配后,寻找左边点中的未盖点进行拓展匈牙利树,将所有遍历到的点标记。这样左边中为标记的,右边的标记的点即为最小点覆盖集。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
#include <algorithm>
#define ll long long
#define INF 1e30
#define inf -2139062144
#define MOD 20071027
#define MAXN 1005
using namespace std;
bool gl[MAXN][MAXN];
bool visy[MAXN],visx[MAXN];
int link[MAXN];
int R,C,N;
int flagx[MAXN],flagy[MAXN];
bool match(int x)
{
flagx[x]=true;
for(int i=1; i<=C; ++i)
if(!visy[i]&&gl[x][i])
{
visy[i]=true;
flagy[i]=true;
if(link[i]==-1||match(link[i]))
{
link[i]=x;
return true;
}
}
return false;
}
int main()
{
while(scanf("%d%d%d",&R,&C,&N))
{
if(!R&&!C&&!N) break;
memset(link,-1,sizeof(link));
memset(gl,0,sizeof(gl));
memset(visx,0,sizeof(visx));
for(int i=0; i<N; ++i)
{
int x,y;
scanf("%d%d",&x,&y);
gl[x][y]=true;
}
int ans=0;
for(int i=1; i<=R; ++i)
{
memset(visy,0,sizeof(visy));
if(match(i)) ans++;
}
memset(flagx,0,sizeof(flagx));
memset(flagy,0,sizeof(flagy));
printf("%d",ans);
for(int i=1; i<=C; ++i)
visx[link[i]]=true;
for(int i=1; i<=R; ++i)
{
memset(visy,0,sizeof(visy));
if(!visx[i]) match(i);
}
for(int i=1; i<=R; ++i)
if(!flagx[i]) printf(" r%d",i);
for(int i=1; i<=C; ++i)
if(flagy[i]) printf(" c%d",i);
printf("\n");
}
return 0;
}