[BZOJ1412/Luogu2598][ZJOI2009]狼和羊的故事

题目链接:

BZOJ1412

Luogu2598

题意好迷。。

一个比较简单的最小割模型。

对于所有狼与源点连边,羊与汇点连边,容量\(+\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;
}
posted @ 2019-01-01 18:58  LanrTabe  阅读(142)  评论(0编辑  收藏  举报