洛谷P1263 || 巴蜀2311 宫廷守卫

 

题目描述

从前有一个王国,这个王国的城堡是一个矩形,被分为M×N个方格。一些方格是墙,而另一些是空地。这个王国的国王在城堡里设了一些陷阱,每个陷阱占据一块空地。

一天,国王决定在城堡里布置守卫,他希望安排尽量多的守卫。守卫们都是经过严格训练的,所以一旦他们发现同行或同列中有人的话,他们立即向那人射 击。因此,国王希望能够合理地布置守卫,使他们互相之间不能看见,这样他们就不可能互相射击了。守卫们只能被布置在空地上,不能被布置在陷阱或墙上,且一 块空地只能布置一个守卫。如果两个守卫在同一行或同一列,并且他们之间没有墙的话,他们就能互相看见。(守卫就像象棋里的车一样)

你的任务是写一个程序,根据给定的城堡,计算最多可布置多少个守卫,并设计出布置的方案。

输入输出格式

输入格式:

第一行两个整数M和N(1≤M,N≤200),表示城堡的规模。

接下来M行N列的整数,描述的是城堡的地形。第i行j列的数用ai,j表示。

ai,j=0,表示方格[i,j]是一块空地;

ai,j=1,表示方格[i,j]是一个陷阱;

ai,j=2,表示方格[i,j]是墙。

输出格式:

第一行一个整数K,表示最多可布置K个守卫。

此后K行,每行两个整数xi和yi,描述一个守卫的位置。

输入输出样例

输入样例#1:
3 4
2 0 0 0
2 2 2 1
0 1 0 2
输出样例#1:
2
1 2
3 3

说明

样例数据如图5-2(黑色方格为墙,白色方格为空地,圆圈为陷阱,G表示守卫)

 

刚开始的写法是BFS出每个联通块,单独处理。

↑明显不对

后来发现可以把被墙隔开的同一行当成两行处理,列同理。

然后二分图匹配坐标即可。

对二分图的理解还是不透彻啊……

 

(代码仅提供思路,不保证正确性)(没有SPJ程序)

(巴蜀OJ上只要求输出最多守卫数量,已AC)

  1 /*By SilverN*/
  2 #include<iostream>
  3 #include<cstdio>
  4 #include<cmath>
  5 #include<cstring>
  6 #include<algorithm>
  7 #define LL long long
  8 using namespace std;
  9 const int mxn=2200;
 10 int read(){
 11     int x=0,f=1;char ch=getchar();
 12     while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 13     while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
 14     return x*f;
 15 }
 16 struct edge{
 17     int v,nxt;
 18 }e[mxn*100];
 19 int hd[mxn*10],mct=0;
 20 void add_edge(int u,int v){
 21     e[++mct].v=v;e[mct].nxt=hd[u];hd[u]=mct;return;
 22 }
 23 int mp[mxn][mxn];
 24 int idx,idy;
 25 int hs[mxn][mxn];
 26 int aidx[mxn*10],aidy[mxn*10];
 27 int link[mxn*10],vis[mxn*10];
 28 //
 29 int n,m,ans=0;
 30 bool DFS(int u){
 31     for(int i=hd[u];i;i=e[i].nxt){
 32         int v=e[i].v;
 33         if(!vis[v]){
 34             vis[v]=1;
 35             if(link[v]==-1 || DFS(link[v])){
 36                 link[v]=u;
 37                 return 1;
 38             }
 39         }
 40     }
 41     return 0;
 42 }
 43 void solve(){
 44     memset(link,-1,sizeof link);
 45     for(int i=1;i<=idx;i++){
 46         memset(vis,0,sizeof vis);
 47         if(DFS(i))ans++;
 48     }
 49     return;
 50 }
 51 void init(){
 52     int i,j;
 53     bool flag=1;
 54     for(i=1;i<=m;i++){
 55         flag=1;
 56         for(j=1;j<=n;j++){
 57             if(mp[i][j]==2){flag=1;continue;}
 58             if(flag){
 59                 ++idx;
 60                 aidx[idx]=i;
 61                 flag=0;
 62             }
 63             hs[i][j]=idx;
 64         }
 65     }
 66     idy=idx;
 67     for(j=1;j<=n;j++){
 68         flag=1;
 69         for(i=1;i<=m;i++){
 70             if(mp[i][j]==2){flag=1;continue;}
 71             if(flag){
 72                 ++idy;
 73                 aidy[idy]=j;
 74                 flag=0;
 75             }
 76             if(mp[i][j])continue;
 77             add_edge(idy,hs[i][j]);
 78             add_edge(hs[i][j],idy);
 79 //            printf("%d %d:%d\n",i,j,idy);
 80         }
 81     }
 82     return;
 83 }
 84 int main(){
 85     m=read();n=read();
 86     int i,j;
 87     for(i=1;i<=m;i++)
 88         for(j=1;j<=n;j++)
 89             mp[i][j]=read();
 90     init();
 91     solve();
 92 /*    for(i=1;i<=m;i++){
 93      for(j=1;j<=n;j++){
 94          printf("%d ",hs[i][j]);
 95      }
 96      printf("\n");
 97     }*/
 98     printf("%d\n",ans);
 99     for(i=idx+1;i<=idy;i++){
100         if(link[i]!=-1)printf("%d %d\n",aidx[link[i]],aidy[i]);
101     }
102     return 0;    
103 }

 

 

转载于:https://www.cnblogs.com/SilverNebula/p/6035375.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值