题目描述
从前有一个王国,这个王国的城堡是一个矩形,被分为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
不能放棋的地方之间不放即可。如果没有墙,那么就是很裸的最大匹配问题。
现在有墙,那么我们就直接把区域分块即可,对每一个块跑最大匹配。每一个点都由能到达的最小面的点,和能到达的最右边的点决定。
那么,输出结果很麻烦,最后恼羞成怒,直接前向星记录点的坐标了,懒得坐标变换。
借用图片:
图片中的点,就是我们前向星的坐标。
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
const int N=1e5+10,M=1e6+10;
const int dx[]={0,1,0,-1},dy[]={1,0,-1,0};
int g[210][210],n,m,s,t,h[N],res,base=40000;
int head[N],nex[M],to[M],w[M],tot,x[M],y[M];
inline void ade(int a,int b,int c,int e,int f){
to[++tot]=b; nex[tot]=head[a]; w[tot]=c; head[a]=tot; x[tot]=e; y[tot]=f;
}
inline void add(int a,int b,int c,int e,int f){ade(a,b,c,e,f); ade(b,a,0,e,f);}
inline int bfs(){
queue<int> q; q.push(s); memset(h,0,sizeof h); h[s]=1;
while(q.size()){
int u=q.front(); q.pop();
for(int i=head[u];i;i=nex[i]){
if(w[i]&&!h[to[i]]){
h[to[i]]=h[u]+1; q.push(to[i]);
}
}
}
return h[t];
}
int dfs(int x,int f){
if(x==t) return f; int fl=0;
for(int i=head[x];i&&f;i=nex[i]){
if(w[i]&&h[to[i]]==h[x]+1){
int mi=dfs(to[i],min(w[i],f));
w[i]-=mi,w[i^1]+=mi,fl+=mi,f-=mi;
}
}
if(!fl) h[x]=-1;
return fl;
}
inline int dinic(){
int res=0;
while(bfs()) res+=dfs(s,inf);
return res;
}
inline void init(){tot=1; memset(head,0,sizeof head);}
inline int id(int x,int y){return (x-1)*m+y;}
int main(){
cin>>n>>m; t=base*2+1; init();
for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) cin>>g[i][j];
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(g[i][j]==2) continue;
if(g[i+1][j]==2||i==n) add(s,id(i,j),1,0,0);
if(g[i][j+1]==2||j==m) add(id(i,j)+base,t,1,0,0);
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(g[i][j]!=0) continue;
int k=j; while(g[i][k+1]!=2&&k+1<=m) k++;
int p=i; while(g[p+1][j]!=2&&p+1<=n) p++;
add(id(p,j),id(i,k)+base,1,i,j);
}
}
cout<<dinic()<<'\n';
for(int i=2;i<=tot;i+=2){
if(w[i^1]&&x[i]&&y[i]) printf("%d %d\n",x[i],y[i]);
}
return 0;
}
来源:CSDN
作者:青烟绕指柔!
链接:https://blog.csdn.net/weixin_43826249/article/details/103781108