【ZJOI2009】[JZOJ1637] 狼和羊的故事

Description

“狼爱上羊啊爱的疯狂,谁让他们真爱了一场;狼爱上羊啊并不荒唐,他们说有爱就有方向......”
  Orez听到这首歌,心想:狼和羊如此和谐,为什么不尝试羊狼合养呢?说干就干!
  Orez的羊狼圈可以看作一个n*m个矩阵格子,这个矩阵的边缘已经装上了篱笆。可是Drake很快发现狼再怎么也是狼,它们总是对羊垂涎三尺,那首歌只不过是一个动人的传说而已。所以Orez决定在羊狼圈中再加入一些篱笆,还是要将羊狼分开来养。
  通过仔细观察,Orez发现狼和羊都有属于自己领地,若狼和羊们不能呆在自己的领地,那它们就会变得非常暴躁,不利于他们的成长。
  Orez想要添加篱笆的尽可能的短。当然这个篱笆首先得保证不能改变狼羊的所属领地,再就是篱笆必须修筑完整,也就是说必须修建在单位格子的边界上并且不能只修建一部分。

文件的第一行包含两个整数n和m。接下来n行每行m个整数,1表示该格子属于狼的领地,2表示属于羊的领地,0表示该格子不是任何一只动物的领地。

10%的数据 n,m≤3
30%的数据 n,m≤20
100%的数据 n,m≤100

Time limits 10s

Solution

很明显这是最小割的模型。

源点向羊,狼向汇点连无限大的边,然后中间的领地交界随便连为1的就行。

注意空地和空地要双向连

Code

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
#define N 50005
#define M 105
#define INF 2147483647
using namespace std;
int fs[N],lt[N],dt[2*N],nt[2*N],f[2*N],zs[2*N],map[M][M],wf[N],sp[N],h[N],vh[N],n,m,n1,m1,fx[4][2]={{1,0},{-1,0},{0,1},{0,-1}};
int nd(int x,int y)
{
    return (x-1)*m+y+1;
}
void link(int x,int y)
{
    dt[++m1]=y;
    if (fs[x]==0) fs[x]=m1;
    lt[x]=nt[lt[x]]=m1;
}
int GAP(int k,int s)
{
    if (k==n1) return s;
    int i,j,l,mh=n1+1;
    for(i=fs[k];i>0;i=nt[i])
    {
        int p=dt[i];
        if (f[i]>0)
        {
            if (h[k]==h[p]+1)
            {
                l=GAP(p,min(s,f[i]));
                if (l>0)
                {
                    f[i]-=l;
                    f[zs[i]]+=l;
                    return l;
                }
                if (h[1]>n1) return 0;
            }
            mh=min(mh,h[p]+1);
        }
    }
    vh[h[k]]--;
    if (vh[h[k]]==0) h[1]=n1+1;
    vh[h[k]=mh]++;
    return 0;
}
int main()
{
    cin>>n>>m;
    int i,j,k,ans=0;
    fo(i,1,n)
    {
        fo(j,1,m)
        {
            scanf("%d",&map[i][j]);
            int v=nd(i,j);
            if (map[i][j]==1) wf[++wf[0]]=v;
            if (map[i][j]==2) sp[++sp[0]]=v;
        }
    }
    fo(i,1,n)
    {
        fo(j,1,m)
        {   
            fo(k,0,3)
            {
                int x=i+fx[k][0],y=j+fx[k][1];
                if (x>0&&y>0&&x<=n&&y<=m&&(map[i][j]!=map[x][y]||map[i][j]==0))
                {
                    int p=nd(i,j),q=nd(x,y);
                    if (p<q)
                    {
                        link(p,q);
                        if (map[i][j]==2||(map[i][j]==0&&map[x][y]==1)||(map[i][j]==map[x][y]&&map[i][j]==0)) f[m1]=1;
                        link(q,p);
                        if (map[x][y]==2||(map[x][y]==0&&map[i][j]==1)||(map[i][j]==map[x][y]&&map[i][j]==0)) f[m1]=1;
                        zs[m1]=m1-1;
                        zs[m1-1]=m1;
                    }
                }
            }
        }
    }
    n1=n*m+2;
    fo(i,1,sp[0])
    {
        link(1,sp[i]);
        f[m1]=n1;
        link(sp[i],1);
        zs[m1]=m1-1;
        zs[m1-1]=m1;
    } 
    fo(i,1,wf[0])
    {
        link(n1,wf[i]);
        link(wf[i],n1);
        f[m1]=n1;
        zs[m1]=m1-1;
        zs[m1-1]=m1;
    }
    memset(h,0,sizeof(h));
    vh[0]=n1;
    while(h[1]<=n1) 
        ans+=GAP(1,INF);
    cout<<ans;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值