ZJOI2009 狼和羊的故事

题目大意

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

Input

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

Output

文件中仅包含一个整数ans,代表篱笆的最短长度。

Sample Input

2 2
2 2
1 1

Sample Output

2

数据范围

10%的数据 nm3
30%的数据 nm20
100%的数据 nm100

思路分析

这道题目不难想到是一道最小割的题。我们要使得羊和狼不存在路,就可以转换为羊集和狼集不从在通路,那么这样就转换成了一个最小割的题目了。

连边方法

(S) (1) (,1) (T,) .

参考代码

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define maxn 105
#define oo 100000000
using namespace std;

const int fx[4][2]={{-1,0},{1,0},{0,1},{0,-1}};

int a[maxn*maxn],head[maxn*maxn],t[maxn*maxn*10],next[maxn*maxn*10],v[maxn*maxn*10],sum;

int n,m;

int vh[maxn*maxn],pre[maxn*maxn],his[maxn*maxn],di[maxn*maxn],dis[maxn*maxn];

void insert(int x,int y,int z){
    t[++sum]=y;
    v[sum]=z;
    next[sum]=head[x];
    head[x]=sum;
}

int main(){
    scanf("%d%d",&n,&m);
    fo(i,1,n*m) scanf("%d",&a[i]);
    sum=1;
    fo(i,1,n)
        fo(j,1,m){
            int w=(i-1)*m+j;
            if (a[w]==1){
                insert(0,w,oo);
                insert(w,0,0);
                fo(k,0,3){
                    int xx=i+fx[k][0],yy=j+fx[k][1];
                    if (xx<=0||yy<=0||xx>n||yy>m||a[(xx-1)*m+yy]==1) continue;
                    insert(w,(xx-1)*m+yy,1);
                    insert((xx-1)*m+yy,w,0);
                }
            }
            else if (a[w]==0){
                fo(k,0,3){
                    int xx=i+fx[k][0],yy=j+fx[k][1];
                    if (xx<=0||yy<=0||xx>n||yy>m||a[(xx-1)*m+yy]==1) continue;
                    insert(w,(xx-1)*m+yy,1);
                    insert((xx-1)*m+yy,w,0);
                }
            }
            else {
                insert(w,n*m+1,oo);
                insert(n*m+1,w,0);
            }
        }
    bool p;
    int tot=n*m+2,x=0,aug=oo,ans=0;
    vh[0]=tot;
    while (dis[0]<tot){
        p=0;
        his[x]=aug;
        for(int tmp=di[x];tmp;tmp=next[tmp]){
            if (v[tmp]&&dis[t[tmp]]+1==dis[x]){
                p=1;
                di[x]=tmp;
                pre[t[tmp]]=x;
                aug=min(aug,v[tmp]);
                x=t[tmp];
                if (x==n*m+1){
                    ans+=aug;
                    while (x) {
                        int last=pre[x];
                        v[di[last]]-=aug;
                        v[di[last] ^ 1]+=aug;
                        x=last;
                    }
                }
                break;
            }
        }
        if (p) continue;
        int k,min=tot;
        for(int tmp=head[x];tmp;tmp=next[tmp])
            if (dis[t[tmp]]<min&&v[tmp]) min=dis[t[tmp]],k=tmp;
        if (--vh[dis[x]]==0) break;
        vh[++min]++;
        dis[x]=min;
        di[x]=k;
        if (x){
            x=pre[x];
            aug=his[x];
        }
    }
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值