ZOJ 1654 Place the Robots(最大二分匹配)

 

这道题题意和ZOJ 1002 Fire Net 一样,只不过是数据规模较大(50*50,而1002只有4*4),所以1002可以直接暴搜解决问题,这个用搜索肯定不行。思路是转化成二分图,求最大匹配。

 

每个 'o' 点可以放置机器人,如果一个点放置一个机器人后,它能影响到的范围(上下左右四个方向,遇到边界,或者 '#' 截止)内就不能再放机器人,所以以能放置机器人的区域的横坐标为 x 集合,以纵坐标为 y 集合构图,求最大二分匹配即可

 

ps:有点要注意,以这种方法构图的话,矩阵中有几个 'o' 点,二分图中就有几条边。这点可以用来检验构图的正确性

 

#include<stdio.h> #include<string.h> #define N 55 #define M 2555 struct node { int x,y; }p[N][N]; //p[i][j].x 和 p[i][j].y 分别表示矩阵中点(i,j)对应的二分图中x,y集合中的编号 char cap[N][N]; int map[M][M]; int match[M],vis[M],size_x,size_y; void init() { int i,j,k,l,r,u,d,n,m; scanf("%d%d",&n,&m); getchar(); memset(cap,0,sizeof(cap)); for(i=0;i<n;i++) gets(cap[i]); for(i=0;i<n;i++) { for(j=0;j<m;j++) p[i][j].x=p[i][j].y=0; } size_x=0; //可用的横坐标数,也是x集合的大小 size_y=0; //同理,是y集合的大小 for(i=0;i<n;i++) { for(j=0;j<m;j++) { if(cap[i][j]=='o') { if(p[i][j].x==0) //是否处理过,也就是说是否在前面出现的 'o' 点的影响范围内 { size_x++; k=j; while(k>=0 && cap[i][k]!='#') k--; l=++k; k=j; while(k<m && cap[i][k]!='#') k++; r=--k; for(k=l;k<=r;k++) p[i][k].x=size_x; } if(p[i][j].y==0) { size_y++; k=i; while(k>=0 && cap[k][j]!='#') k--; u=++k; k=i; while(k<n && cap[k][j]!='#') k++; d=--k; for(k=u;k<=d;k++) p[k][j].y=size_y; } } } } memset(map,0,sizeof(map)); for(i=0;i<n;i++) { for(j=0;j<m;j++) if(cap[i][j]=='o') map[ p[i][j].x ][ p[i][j].y ]=1; } } int dfs(int x) { int i; for(i=1;i<=size_y;i++) { if(!vis[i] && map[x][i]) { vis[i]=1; if(!match[i] || dfs(match[i])) { match[i]=x; return 1; } } } return 0; } int main() { int i,t,cnt,time=1; scanf("%d",&t); while(t--) { init(); memset(match,0,sizeof(match)); cnt=0; for(i=1;i<=size_x;i++) { memset(vis,0,sizeof(vis)); if(dfs(i))cnt++; } printf("Case :%d/n",time++); printf("%d/n",cnt); } return 0; } 

 

你可能感兴趣的:(ZOJ 1654 Place the Robots(最大二分匹配))