bzoj 1711 最大流

10 篇文章 0 订阅

题意:n头牛,m1种吃的,m3种喝的,每头牛有nm1种喜欢的吃的、nm2种喜欢的喝的,满足一头牛的条件是 :既给他一个爱吃的也给他一个爱喝的。(注意如果一头牛没有爱吃的或爱喝的,是永远不能满足的)。每种吃的和喝的能且只能用一次,求最多满足多少牛

最大流还是比较显然的,但是建图还是相当经(恶)典(心)的

给出正确的建图:

s -> 每种吃的 容量为1 (保证每种吃的能且只能用一次)

每种喝的 --> T 容量为1 (保证每种喝的能且只能用一次)

每头牛i拆点 i -> i' 容量为1 (保证每头牛只能满足一次)

第i头牛喜欢吃的 -> i 容量为1

i‘ - >’第i头牛喜欢喝的 容量为1

跑最大流即可



个人吐槽(屡战屡败的建图经历):

教练我要学建图QAQ

以下大雾

(1)第一个naive的建图

S -> 每头牛i 容量为1

每头牛 -> 爱吃的 容量为1

第i头牛爱吃的 -> 第i头牛爱喝的 容量为1

第i头牛爱喝的 -> T 容量为1

亲测:60分  

虽然每种喝的保证最多用一次,每头牛最多满足一次

but,会出现一种情况:多头牛喜欢一种吃的,而同时又喜欢多种喝的,那么这种吃的会被重复使用,所以答案会大 orz

所以要严格限制吃的和喝的,就把吃的放源,喝的放汇,中间是牛,然后就有了第二个naive的图

(2)第二个naive的建图

S -> 每种吃的 容量为1

每种喝的 -> T 容量为1

第i头牛爱吃的 -> 第i头牛 容量为1

第i头牛 -> 第i头牛爱喝的 容量为1

虽然每种吃的和喝的都保证了最多用一次,

but,会出现一种情况:一头牛喜欢多种吃的和多种喝的,那么这头牛就是被多次满足记在答案中,所以答案会大 orz

所以要严格限制一头牛只被满足一次,就把牛拆点中间连1的容量来限流,这样才能保证牛也只被满足一次

感谢zzc为我这个深陷错误无法自拔的蒟蒻指出错误反例...


var
        n,m1,m2,l,ss,st :longint;
        last,dis,que    :array[0..410] of longint;
        nm1,nm2,ans,x   :longint;
        i,j             :longint;
        pre,other,len   :array[0..21010] of longint;
function min(a,b:longint):longint;
begin
   if a<b then exit(a) else exit(b);
end;

procedure connect(x,y,z:longint);
begin
   inc(l);
   pre[l]:=last[x];
   last[x]:=l;
   other[l]:=y;
   len[l]:=z;
end;

function bfs:boolean;
var
        h,tl,p,q,cur:longint;
begin
   fillchar(dis,sizeof(dis),0);
   h:=0; tl:=1; que[1]:=ss; dis[ss]:=1;
   while (h<>tl) do
   begin
      h:=h mod 305+1;
      cur:=que[h];
      q:=last[cur];
      while (q<>0) do
      begin
         p:=other[q];
         if (dis[p]=0) and (len[q]>0) then
         begin
            dis[p]:=dis[cur]+1;
            tl:=tl mod 305+1;
            que[tl]:=p;
            if p=st then exit(true);
         end;
         q:=pre[q];
      end;
   end;
   exit(false);
end;

function dinic(x,flow:longint):longint;
var
        p,q,tt,rest:longint;
begin
   if x=st then exit(flow);
   rest:=flow;
   q:=last[x];
   while (q<>0) do
   begin
      p:=other[q];
      if (dis[p]=dis[x]+1) and (len[q]>0) and (rest>0) then
      begin
         tt:=dinic(p,min(rest,len[q]));
         dec(len[q],tt);
         dec(rest,tt);
         inc(len[q xor 1],tt);
         if rest=0 then exit(flow);
      end;
      q:=pre[q];
   end;
   if rest=flow then dis[x]:=0;
   exit(flow-rest);
end;


begin
   read(n,m1,m2);
   ss:=2*n+m1+m2+1; st:=ss+1; l:=1;
   for i:=1 to m1 do
   begin
      connect(ss,i,1);
      connect(i,ss,0);
   end;
   for i:=1 to n do
   begin
      connect(m1+i,m1+n+i,1);
      connect(m1+n+i,m1+i,0);
   end;
   for i:=1 to n do
   begin
      read(nm1,nm2);
      for j:=1 to nm1 do
      begin
         read(x);
         connect(x,m1+i,1);
         connect(m1+i,x,0);
      end;
      for j:=1 to nm2 do
      begin
         read(x);
         connect(m1+n+i,m1+2*n+x,1);
         connect(m1+2*n+x,m1+n+i,0);
      end;
   end;
   //
   for i:=1 to m2 do
   begin
      connect(m1+2*n+i,st,1);
      connect(st,m1+2*n+i,0);
   end;
   ans:=0;
   while bfs do inc(ans,dinic(ss,maxlongint div 10));
   writeln(ans);
end.
—— by Eirlys



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值