题目大意:在一个n*m的网格中,有k个目标,现在可以任选一行或列消除在其上的所有目标,求出最少选择次数及选法。
题目分析:经典的最小点覆盖问题,并且输出一个最小点覆盖集。在求出最大匹配之后,以未覆盖的x点进行标记,沿着未覆盖->覆盖->未覆盖->覆盖...的路径标记,最后x中未标记的和y中标记的点构成最小点覆盖集。
代码如下:
# include<iostream> # include<cstdio> # include<cstring> # include<algorithm> using namespace std; # define REP(i,s,n) for(int i=s;i<n;++i) # define CL(a,b) memset(a,b,sizeof(a)) # define CLL(a,b,n) fill(a,a+n,b) const int N=1005; struct Edge { int to,nxt; }; Edge e[N*N]; int link[N],visx[N],visy[N],vis[2*N],mark[N]; int cnt,n,m,head[N]; void add(int u,int v) { e[cnt].to=v; e[cnt].nxt=head[u]; head[u]=cnt++; } void dfs1(int x) { visx[x]=1; for(int i=head[x];i!=-1;i=e[i].nxt){ int y=e[i].to; if(visy[y]) continue; visy[y]=1; dfs1(link[y]); } } bool dfs(int x) { for(int i=head[x];i!=-1;i=e[i].nxt){ int y=e[i].to; if(vis[y]) continue; vis[y]=1; if(link[y]==-1||dfs(link[y])){ link[y]=x; return true; } } return false; } int match() { int res=0; REP(i,1,n+1){ CL(vis,0); if(dfs(i)) ++res; } return res; } int main() { int a,b,k; while(scanf("%d%d%d",&n,&m,&k)&&(n+m+k)) { cnt=0; CL(head,-1); CL(link,-1); CL(mark,0); CL(visx,0); CL(visy,0); while(k--) { scanf("%d%d",&a,&b); add(a,b); } int ans=match(); REP(i,1,m+1) if(link[i]!=-1) mark[link[i]]=1; REP(i,1,n+1) if(!mark[i]) dfs1(i); printf("%d",ans); REP(i,1,n+1) if(!visx[i]) printf(" r%d",i); REP(i,1,m+1) if(visy[i]) printf(" c%d",i); printf(" "); } return 0; }