题目大意:
一个N*N的网格中(N<=4),有些格子是墙。问在这个网格中最多能放置多少个士兵,使得他们在四个方向上不能相互攻击。
分析:
这是一个古老的题目了,我最开始学ACM的时候一眼就能看出来这是一个搜索题,并且也能毫不费力地写出一段搜索代码把它过掉。
后来听罗老师讲了这个题之后才知道,原来还有更高效的匹配算法来解决这个问题。(按照罗老师的原话说,这个题目出题的本意就是让大家用匹配来做……>_<)
可以这样来构造匹配模型。假设没有墙阻挡,那么把地图的行和列作为二部图的两侧顶点,i行和j列有交叉,那么二部图中g[i][j]=1。现在有墙隔开,如果墙把第i行分割成了2段,那么二部图左边节点i就拆成两个点i和i',若i'和j有交叉,那么二部图中g[i'][j]=1。列的拆分同理。
这样得到的二部图,求出一个最大匹配即得到了最多的放置士兵数。
代码也容易写出,感觉自己写得不是很精炼。
- /*
- ZJU1002 Pro
- */
- #include <stdio.h>
- #include <memory.h>
- #define clr(a) memset(a,0,sizeof(a))
- #define N 8
- int find(int i,int m,int g[][N],int mat[],int tmat[]){
- int v,j;
- for(j=0;j<m;j++) if(g[i][j]&&tmat[j]==0){
- tmat[j]=1;v=mat[j];mat[j]=i;
- if(v==-1||find(v,m,g,mat,tmat)) return 1;
- mat[j]=v;
- }
- return 0;
- }
- int match(int g[][N],int n,int m){
- int i,k=0,tmat[N],mat[N];
- for(i=0;i<m;i++) mat[i]=-1;
- for(i=0;i<n;i++){clr(tmat); k+=find(i,m,g,mat,tmat);}
- return k;
- }
- int main()
- {
- int i,j,k,w,n,m;
- char map[N][N];
- char a[N][N],fa;
- char b[N][N],fb;
- int g[N][N];
- while(scanf("%d",&w),w){
- //init
- clr(g); n=m=0;
- //input
- for(i=0;i<w;i++){
- scanf("%s",map[i]);
- }
- //create a b map
- for(i=0;i<w;i++){
- fa=0; fb=0;
- for(j=0;j<w;j++){
- if(map[i][j]=='.'){
- if(fa==0) n++;
- fa=1; a[i][j]=n;
- }
- else fa=0;
- if(map[j][i]=='.'){
- if(fb==0) m++;
- fb=1; b[j][i]=m;
- }
- else fb=0;
- }
- }
- //create g
- for(i=0;i<w;i++) for(j=0;j<w;j++){
- if(map[i][j]=='.'){
- g[a[i][j]-1][b[i][j]-1]=1;
- }
- }
- //matching
- k=match(g,n,m);
- printf("%d/n",k);
- }
- return 0;
- }