ZJU 1002 Fire Net - 二分图最大匹配

题目大意:

一个N*N的网格中(N<=4),有些格子是墙。问在这个网格中最多能放置多少个士兵,使得他们在四个方向上不能相互攻击。

分析:

这是一个古老的题目了,我最开始学ACM的时候一眼就能看出来这是一个搜索题,并且也能毫不费力地写出一段搜索代码把它过掉。

后来听罗老师讲了这个题之后才知道,原来还有更高效的匹配算法来解决这个问题。(按照罗老师的原话说,这个题目出题的本意就是让大家用匹配来做……>_<)

可以这样来构造匹配模型。假设没有墙阻挡,那么把地图的行和列作为二部图的两侧顶点,i行和j列有交叉,那么二部图中g[i][j]=1。现在有墙隔开,如果墙把第i行分割成了2段,那么二部图左边节点i就拆成两个点i和i',若i'和j有交叉,那么二部图中g[i'][j]=1。列的拆分同理。

这样得到的二部图,求出一个最大匹配即得到了最多的放置士兵数。

代码也容易写出,感觉自己写得不是很精炼。

 

  1. /*
  2. ZJU1002 Pro
  3. */
  4. #include <stdio.h>
  5. #include <memory.h>
  6. #define clr(a) memset(a,0,sizeof(a))
  7. #define N 8
  8. int find(int i,int m,int g[][N],int mat[],int tmat[]){
  9.     int v,j;
  10.     for(j=0;j<m;j++) if(g[i][j]&&tmat[j]==0){
  11.         tmat[j]=1;v=mat[j];mat[j]=i;
  12.         if(v==-1||find(v,m,g,mat,tmat)) return 1;
  13.         mat[j]=v;
  14.     }
  15.     return 0;
  16. }
  17. int match(int g[][N],int n,int m){
  18.     int i,k=0,tmat[N],mat[N];
  19.     for(i=0;i<m;i++) mat[i]=-1;
  20.     for(i=0;i<n;i++){clr(tmat); k+=find(i,m,g,mat,tmat);}
  21.     return k;
  22. }
  23. int main()
  24. {
  25.     int i,j,k,w,n,m;
  26.     char map[N][N];
  27.     char a[N][N],fa;
  28.     char b[N][N],fb;
  29.     int g[N][N];
  30.     
  31.     while(scanf("%d",&w),w){
  32.         //init
  33.         clr(g); n=m=0;
  34.         //input
  35.         for(i=0;i<w;i++){
  36.             scanf("%s",map[i]);
  37.         }
  38.         //create a b map
  39.         for(i=0;i<w;i++){
  40.             fa=0; fb=0;
  41.             for(j=0;j<w;j++){
  42.                 if(map[i][j]=='.'){
  43.                     if(fa==0) n++;
  44.                     fa=1; a[i][j]=n;
  45.                 }
  46.                 else fa=0;
  47.                 if(map[j][i]=='.'){
  48.                     if(fb==0) m++;
  49.                     fb=1; b[j][i]=m;
  50.                 }
  51.                 else fb=0;
  52.             }
  53.         }
  54.         //create g
  55.         for(i=0;i<w;i++) for(j=0;j<w;j++){
  56.             if(map[i][j]=='.'){
  57.                 g[a[i][j]-1][b[i][j]-1]=1;
  58.             }
  59.         }
  60.         //matching
  61.         k=match(g,n,m);
  62.         printf("%d/n",k);
  63.     }
  64.     return 0;
  65. }

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值