题意: 给你一个农场 例如
*.*. .*** ***. ..*.
其中 *代表障碍物, . 代表空地,你可以用宽度为1 长度不限的木板去覆盖这些障碍,木块可以重叠,问你最少需要多少木块
可以把所有 * 覆盖掉。
分析: 二分图最小点权覆盖,这道题建图比较特别,之前的 POJ 3041 是一次覆盖一行或一列,可以 把行看成 X集合, 把列看成是 Y 集合,进行二分图匹配,但是这题每一行或每一列的
障碍物可能是不连续的, 所以要把同一行的各个连续的障碍段分开进行建图,可以把每一行或每一列连续的一段标记成同一个序号,每一个点看成是行这一段的障碍和列这一段障碍的边,
建立关系,最后求出最大匹配即最小覆盖。
View Code
#include<stdio.h> #include<string.h> #define clr(x)memset(x,0,sizeof(x)) struct node { int to,next; }q[100000]; int head[300]; int tot; int link[1000]; int v[1000]; void add(int s,int u) { q[tot].to=u; q[tot].next=head[s]; head[s]=tot++; } int a[3000]; int b[3000]; int vis[55][55]; char map[55][55]; int ma[55][55]; int mb[55][55]; int find(int x) { int k,i; for(i=head[x];i;i=q[i].next) { k=q[i].to; if(!v[k]) { v[k]=1; if(link[k]==0||find(link[k])) { link[k]=x; return 1; } } } return 0; } int main() { int an,bn,n,m,i,sum,j,k,x; while(scanf("%d%d",&n,&m)!=EOF) { clr(link); clr(head); tot=1; for(i=1;i<=n;i++) scanf("%s",map[i]+1); x=1; an=bn=0; clr(vis); for(i=1;i<=n;i++) for(j=1;j<=m;j++) if(!vis[i][j]&&map[i][j]=='*') { a[an++]=x; for(k=j;k<=m;k++) { if(map[i][k]=='*'){ vis[i][k]=1; ma[i][k]=x; } else break; } x++; } clr(vis); for(i=1;i<=n;i++) for(j=1;j<=m;j++) if(!vis[i][j]&&map[i][j]=='*') { b[bn++]=x; for(k=i;k<=n;k++) { if(map[k][j]=='*'){ vis[k][j]=1; mb[k][j]=x; } else break; } x++; } for(i=1;i<=n;i++) for(j=1;j<=m;j++) if(map[i][j]=='*') add(ma[i][j],mb[i][j]); sum=0; for(i=0;i<an;i++) { clr(v); if(find(a[i])) sum++; } printf("%d\n",sum); } return 0; }