【ZJOI2009】狼和羊的故事

题目

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

题意

给你一个n*m的矩阵,让你求用尽量少的篱笆,把羊和狼分开。

分析

我们可以发现,这题的篱笆,就是把两个集合(羊和狼)一分为二,而且又是费用最小,所以我们自然的想到了最小割。
我们接着考虑建图:
从源点向所有的狼连一条容量为maxlongint的边,然后再从所有的羊向汇点连一条容量为maxlongint的边,狼向周围的空地和羊连容量为1的边,空地往周围空地和羊连一条容量为1的边,这样跑一次最大流便可以了。

代码

var
    n,m,i,j,nu,x,y,t,s,k,ans:longint;
    bd:array[1..4,1..2] of longint=((0,1),(1,0),(-1,0),(0,-1));
    b,las,nex,f,re,d,dis:array[0..200000] of longint;
    a:array[1..100,1..100] of longint;
procedure insert(x,y,z:longint);
begin
    inc(nu);b[nu]:=y;nex[nu]:=las[x];las[x]:=nu;f[nu]:=z;re[nu]:=nu+1;
    inc(nu);b[nu]:=x;nex[nu]:=las[y];las[y]:=nu;f[nu]:=0;re[nu]:=nu-1;
end;
function bfs:boolean;
var l,r,p:longint;
begin
    l:=0;r:=1;fillchar(dis,sizeof(dis),0);dis[0]:=1;d[1]:=0;
    while l<r do begin
        inc(l);p:=las[d[l]];
        while p<>0 do begin
           if (dis[b[p]]=0)and(f[p]>0) then begin
              dis[b[p]]:=dis[d[l]]+1;inc(r);d[r]:=b[p];
           end;p:=nex[p];
        end;
    end;
    exit(dis[t]<>0);
end;
function min(l,r:longint):longint;
begin
    if l<r then exit(l);exit(r);
end;
function dinic(x,y:longint):longint;
var p,o:longint;
begin
    if x=t then exit(y);
    p:=las[x];dinic:=0;
    while p<>0 do begin
        if (f[p]>0)and(dis[b[p]]=dis[x]+1) then begin
           o:=dinic(b[p],min(y,f[p]));
           if o<>0 then begin
              dec(f[p],o);inc(f[re[p]],o);
              dec(y,o);inc(dinic,o);if y=0 then break;
           end;
        end;p:=nex[p];
    end;
end;
begin
    readln(n,m);
    for i:=1 to n do begin
        for j:=1 to m do read(a[i,j]);
        readln;
    end;
    for i:=1 to n do
        for j:=1 to m do begin
            if a[i,j]=1 then insert(0,(i-1)*m+j,maxlongint)
            else if a[i,j]=2 then insert((i-1)*m+j,n*m+1,maxlongint);
            if a[i,j]<>2 then
            for k:=1 to 4 do begin
                x:=i+bd[k,1];y:=j+bd[k,2];
                if (x<1)or(y<1)or(x>n)or(y>m) then continue;
                if a[x,y]<>1 then insert((i-1)*m+j,(x-1)*m+y,1);
            end;
        end;
    t:=n*m+1;
    while bfs do ans:=ans+dinic(0,maxlongint);
    writeln(ans);
end.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值