[BZOJ1412/Luogu2598][ZJOI2009]狼和羊的故事
题目链接:
题意好迷。。
一个比较简单的最小割模型。
对于所有狼与源点连边,羊与汇点连边,容量\(+\infty\)。
对于每个点向四周连边,容量为\(1\),代表联通。
然后跑一遍最小割就行了(羊和狼联通则有\(1\)流量,相当于建栅栏)。
#include <cstdio>
#include <cstring>
#define ID(x,y) (((x)-1)*m+y)
inline int Min(int a,int b){return a<b?a:b;}
inline int Max(int a,int b){return a>b?a:b;}
int n,m,St,Ed;
int Head[10005],Next[150005],To[150005],Val[150005],En=1;
int Dis[10005],Pre[10005],Cur[10005],Cnt[10005];
const int Inf=0x3f3f3f3f,nx[]={-1,1,0,0},ny[]={0,0,-1,1};
inline void Add(int x,int y,int z)
{
Next[++En]=Head[x],To[Head[x]=En]=y,Val[En]=z;
Next[++En]=Head[y],To[Head[y]=En]=x,Val[En]=0;
}
int Augment()
{
int Res=Inf;
for(int x=Ed;x!=St;x=To[Pre[x]^1])Res=Min(Res,Val[Pre[x]]);
for(int x=Ed,i;x!=St;x=To[i^1])Val[i=Pre[x]]-=Res,Val[i^1]+=Res;
return Res;
}
int ISAP()
{
memcpy(Cur,Head,sizeof Cur);
Cnt[0]=Ed;
int Flow=0,x=St;
while(Dis[St]<Ed)
{
if(x==Ed)Flow+=Augment(),x=St;
bool Flag=false;
for(int i=Cur[x],y;i;i=Next[i])
if(Val[i]&&Dis[y=To[i]]+1==Dis[x])
Flag=true,Cur[x]=Pre[y]=i,x=y,i=0;
if(Flag)continue;
int Wd=Ed-1;
for(int i=Head[x];i;i=Next[i])
if(Val[i])Wd=Min(Wd,Dis[To[i]]);
if(!--Cnt[Dis[x]])break;
++Cnt[Dis[x]=Wd+1],Cur[x]=Head[x];
if(x!=St)x=To[Pre[x]^1];
}
return Flow;
}
int main()
{
scanf("%d%d",&n,&m),St=n*m+1,Ed=St+1;
for(int i=1;i<=n;++i)
for(int j=1,x;j<=m;++j)
{
scanf("%d",&x);
if(x==1)Add(St,ID(i,j),Inf);
else if(x==2)Add(ID(i,j),Ed,Inf);
for(int k=0;k<4;++k)
{
int wx=i+nx[k],wy=j+ny[k];
if(wx>=1&&wx<=n&&wy>=1&&wy<=m)Add(ID(i,j),ID(wx,wy),1);
}
}
printf("%d\n",ISAP());
return 0;
}