行为左边点集,列为右边点集,障碍为边,这样消灭所有障碍即每条边都连着一个点。这样选最少的点即可。这样问题就是求解最小点覆盖集,在二分图中等价于二分最大匹配。但是这里还要求出最小点覆盖集。方法是,在求出二分图最大匹配后,寻找左边点中的未盖点进行拓展匈牙利树,将所有遍历到的点标记。这样左边中为标记的,右边的标记的点即为最小点覆盖集。
#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; }