UVA-11419 SAM I AM (最小点覆盖)
题目大意:在一个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("\n");
}
return 0;
}


浙公网安备 33010602011771号