这道题的最小割模型比较明显,要求用最小的花费来使狼和羊不连通。那么我们可以将狼的领地与源点相连,羊的领地与汇点相连,流量均为正无穷。对于羊和狼相邻的格子,我们连一条流量为1的边,对于羊或狼与空地相邻的格子,我们连一条流量为1的边,表示这块空地可能属于狼或羊,对于空地和空地相邻的格子,我们也要连一条流量为1的边,因为狼和羊可能走到空地上相遇,如果出现这种情况我们也要割断这条边。最后跑一边最大流求出最小割即是答案。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define maxn 10005
#define maxm 100005
#define INF 1000000000
int last[maxn],pre[maxm],other[maxm],cap[maxm];
int n,m,a[104][104],l,q[maxm+5],d[maxn],S,T,ans;
int dx[5]={0,0,1,0,-1};
int dy[5]={0,1,0,-1,0};
void connect(int x,int y,int z)
{
pre[l]=last[x];
last[x]=l;
other[l]=y;
cap[l]=z;
l++;
swap(x,y);
pre[l]=last[x];
last[x]=l;
other[l]=y;
cap[l]=0;
l++;
}
bool bfs(void)
{
memset(d,0,sizeof d);
int h=0,t=1;
q[1]=S;d[S]=1;
while (h!=t)
{
h=h%maxm+1;
int u=q[h];
for (int p=last[u];p!=-1;p=pre[p])
{
int v=other[p];
if (d[v]||cap[p]==0) continue;
d[v]=d[u]+1;
if (v==T) return 1;
t=t%maxm+1;
q[t]=v;
}
}
return 0;
}
int dinic(int u,int flow)
{
int rest=flow;
if (u==T) return flow;
for (int p=last[u];p!=-1;p=pre[p])
{
int v=other[p];
if (rest>0&&d[v]==d[u]+1&&cap[p]>0)
{
int temp=dinic(v,min(rest,cap[p]));
rest-=temp;
cap[p]-=temp;
cap[p^1]+=temp;
}
}
return flow-rest;
}
int main()
{
scanf("%d%d",&n,&m);
memset(last,-1,sizeof last);
S=0;T=n*m+1;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
scanf("%d",&a[i][j]);
if (a[i][j]==1) connect(S,(i-1)*m+j,INF);
if (a[i][j]==2) connect((i-1)*m+j,T,INF);
}
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
{
for (int k=1;k<=4;k++)
{
int x=i+dx[k],y=j+dy[k];
if (x>n||x<1||y>m||y<1) continue;
if (a[i][j]==1)
if (a[i][j]!=a[x][y])
connect((i-1)*m+j,(x-1)*m+y,1);
if (a[i][j]==2)
if (a[x][y]==0)
connect((x-1)*m+y,(i-1)*m+j,1);
if (a[i][j]==a[x][y]&&a[i][j]==0)
connect((i-1)*m+j,(x-1)*m+y,1);
}
}
while (bfs())
ans+=dinic(S,INF);
printf("%d\n",ans);
return 0;
}