既可以是一眼题又可以是有些东西的题
一眼就可以看出来,要用最小割把狼和羊分开,那 \(S\) 向狼连 \(inf\),羊向 \(T\) 连 \(inf\),每个点向周围连 1,考虑割掉的边是栅栏
之所以说它有些东西,是应为它可以表示成数学形式,考虑将狼划分成 0 集合,将羊划分成 1 集合,没有归属的点随便
如果两个点不在同一个集合,那么它需要栅栏分开,就是每个点向周围连 1 的边,由此可知,0 的归属就不需要讨论了
#include<bits/stdc++.h>
using namespace std;
#define rg register
inline int read(){
rg char ch=getchar();
rg int x=0,f=0;
while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();
while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return f?-x:x;
}
int head[100010],ver[200010],flow[200010],nxt[200010],tot=1,hh[100010];
int mp[110][110];
int dis[100010],ans;
int dx[]={0,0,1,-1},dy[]={1,-1,0,0};
int n,m,s,t;
inline void add(int x,int y,int f){
ver[++tot]=y;
flow[tot]=f;
nxt[tot]=head[x];
head[x]=tot;
}
inline int get(int i,int j){
return (i-1)*m+j;
}
int bfs(){
queue<int> q;
q.push(s);
for(int i=s;i<=t;++i) hh[i]=head[i];
memset(dis,0,sizeof dis);
dis[s]=1;
while(!q.empty()){
int x=q.front();q.pop();
for(int y,i=head[x];i;i=nxt[i]){
y=ver[i];
if(!dis[y]&&flow[i]){
dis[y]=dis[x]+1;
q.push(y);
}
}
}
return dis[t]?true:false;
}
int dfs(int x,int f){
if(x==t||!f) return f;
int used=0;
for(int y,w,&i=hh[x];i;i=nxt[i]){
y=ver[i];
if(dis[y]==dis[x]+1&&flow[i]){
w=dfs(y,min(f-used,flow[i]));
if(w){
flow[i]-=w;
flow[i^1]+=w;
used+=w;
if(used==f) return f;
}
}
}
if(!used) dis[x]=0;
return used;
}
void dinic(){
while(bfs()) ans+=dfs(s,99999999);
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;++i)
for(int j=1;j<=m;++j)
mp[i][j]=read();
s=0,t=n*m+1;
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
if(mp[i][j]!=1){
for(int k=0;k<4;++k){
int xl=j+dx[k],yl=i+dy[k];
if(xl<1||yl<1||xl>m||yl>n) continue;
add(get(i,j),get(yl,xl),1);
add(get(yl,xl),get(i,j),0);
}
if(mp[i][j]==2){
add(s,get(i,j),99999999);
add(get(i,j),s,0);
}
}else{
add(get(i,j),t,99999999);
add(t,get(i,j),0);
}
}
}
dinic();
cout<<ans<<endl;
return 0;
}