宫廷守卫

◇◆丶佛笑我妖孽 提交于 2019-12-31 20:45:28

题目描述
从前有一个王国,这个王国的城堡是一个矩形,被分为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;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!