[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;
}

猜你喜欢

转载自www.cnblogs.com/LanrTabe/p/10205537.html
今日推荐