• bzoj2502


    学到很多知识的一道题目
    一开始读错题,后来发现是每条边必须至少访问一次
    明显是一个有下界的最小流
    首先是我自己脑补的比较渣的算法,可以无视:
    对于有下界的最小流,我不会做,但是我会做有下界的费用流,而且注意这是一个DAG图
    只要对每条边建x-->y (flow=1 cost=-inf)和x-->y (flow=inf cost=0)这样两条边
    (这是解决DAG图的有下界费用流的非常简便的做法,通过附加边,根据最短路原理一定能保证这条边下界流量被流过)
    然后从源点到每个出度非0的点连一条cost=1 flow=inf的边,出度为0的点到汇点连边flow=inf cost=0
    然后跑最小费用流(注意这里并不是最大流,只要增广到正费用,就代表原图的边都被走过了,可以直接退出)
    最后再加上m*inf即是ans
    但是很可惜,这个方法虽然容易想,但是跑得慢(1100ms) 而最优的解法是8ms,差距啊……
    本着精益求精的精神,下面介绍有下界的网络流
    首先这是一个有源有汇的有下界最小流,要想解决这个问题,就先要解决无源无汇有下界的可行流(循环流)
    无源无汇是什么意思呢,那就表示每个点的出流=入流
    令每条边上界为a,下界为b,则这样
    我们考虑将有下界转化为下界为0的网络流,这样原来每条边的上界变成a-b
    但经过这样更改流量是不守恒的,因此,我们添加源汇为s,t
    令d(u)=该点入下界流和-该点出下界流和
    如果d(u)>0 则连边s-->u flow=d(u)
    如果d(u)<0 则连边u-->t flow=-d(u)
    不难发现,如果要流量满足下界,那必然s延伸出的附加边一定是满流,这是有附加边流量的意义决定的
    因此我们只要做s到t的最大流,然后判断s延伸出的附加边是否满流
    如果满流,则存在循环流,如果不存在,那就无解
    有了这个基础,我们就不难解决有源有汇的有上下界最大流的问题
    首先先转化为已知问题,添加t-->s下界为0,上界为inf的边
    这样就转化为无源无汇有下界的可行流,先按上述方法添加超级源汇ss,tt
    然后判断是否存在可行流,存在则记为f1
    然后再跑s-->t的最大流(这里是改造后的图,即每条边下界为0,上界为a-b)记为f2,
    UPD:注意,这里最大流不是f1+f2而是单独的f2,为什么呢?
    其实我们在跑完可行流之后,有些边的反向弧存了可行流的流量,这些反向弧可以构成s-->t流量为可行流的路径
    所以f2实际上是包含可行流的,具体画一个图就明白了
    容易出问题的是有源有汇的有上下界最小流
    一开始很容易误认为,添加t-->s下界为0,上界为inf的边,做出来的可行流不就是最小流吗?
    实际上是不对的,具体见http://www.cnblogs.com/kane0526/archive/2013/04/05/3001108.html
    (注意那张图1--tt之间的边方向似乎反了)
    可以发现,当出现循环流的时候,可行流会增大。
    因此正确的做法是先不添加t-->s,直接按无源无汇有下界可行流做
    然后再添加t-->s,然后再做ss到tt的最大流,如果附加边满流即有解,解就是t-->s实际流过的流量
    这应该怎么理解呢?其实第一次做最大流就是在消去循环会带来增加的流量
    好再回到这道题目上来,由于这道题目一定有解,而且源点,汇点所连的边下界为0
    我们可以稍稍变动一下算法,具体见程序

      1 const inf=10000;
      2 type node=record
      3        next,point,flow,cost:longint;
      4      end;
      5 
      6 var edge:array[0..400010] of node;
      7     q:array[0..400010] of longint;
      8     p,d,pre,cur:array[0..200] of longint;
      9     v:array[0..200] of boolean;
     10     s,i,len,n,t,j,x,m:longint;
     11 
     12 function min(a,b:longint):longint;
     13   begin
     14     if a>b then exit(b) else exit(a);
     15   end;
     16 
     17 procedure add(x,y,f,c:longint);
     18   begin
     19     inc(len);
     20     edge[len].point:=y;
     21     edge[len].flow:=f;
     22     edge[len].cost:=c;
     23     edge[len].next:=p[x];
     24     p[x]:=len;
     25   end;
     26 
     27 function spfa:boolean;
     28   var y,i,f,r,x:longint;
     29   begin
     30     d[0]:=0;
     31     for i:=1 to t do
     32       d[i]:=inf;
     33     f:=1;
     34     r:=1;
     35     q[1]:=0;
     36     fillchar(v,sizeof(v),false);
     37     v[0]:=true;
     38     while f<=r do
     39     begin
     40       x:=q[f];
     41       v[x]:=false;
     42       i:=p[x];
     43       while i<>-1 do
     44       begin
     45         y:=edge[i].point;
     46         if edge[i].flow>0 then
     47           if d[y]>d[x]+edge[i].cost then
     48           begin
     49             d[y]:=d[x]+edge[i].cost;
     50             pre[y]:=x;
     51             cur[y]:=i;
     52             if not v[y] then
     53             begin
     54               inc(r);
     55               q[r]:=y;
     56               v[y]:=true;
     57             end;
     58           end;
     59         i:=edge[i].next;
     60       end;
     61       inc(f);
     62     end;
     63     if d[t]=inf then exit(false) else exit(true);
     64   end;
     65 
     66 function mincost:longint;
     67   var i,j:longint;
     68   begin
     69     mincost:=0;
     70     while spfa do
     71     begin
     72       if d[t]>0 then exit;  //关键
     73       i:=t;
     74       while i<>0 do
     75       begin
     76         j:=cur[i];
     77         dec(edge[j].flow);
     78         inc(edge[j xor 1].flow);
     79         i:=pre[i];
     80       end;
     81       mincost:=mincost+d[t];
     82     end;
     83   end;
     84 
     85 begin
     86   len:=-1;
     87   fillchar(p,sizeof(p),255);
     88   readln(n);
     89   t:=n+1;
     90   for i:=1 to n do
     91   begin
     92     read(s);
     93     m:=m+s;
     94     for j:=1 to s do
     95     begin
     96       read(x);
     97       add(i,x,1,-inf);
     98       add(x,i,0,inf);
     99       add(i,x,inf,0);
    100       add(x,i,0,0);
    101     end;
    102     if s=0 then
    103     begin
    104       add(i,t,inf,0);
    105       add(t,i,0,0);
    106     end
    107     else begin
    108       add(0,i,inf,1);
    109       add(i,0,0,-1);
    110     end;
    111   end;
    112   writeln(mincost+m*inf);
    113 end.
    脑补的做法
      1 const inf=100007;
      2 type node=record
      3        next,point,flow:longint;
      4      end;
      5 
      6 var edge:array[0..400010] of node;
      7     p,d,pre,cur,numh,h,c:array[0..200] of longint;
      8     v:array[0..200] of boolean;
      9     s,i,len,n,t,j,x,m:longint;
     10 
     11 function min(a,b:longint):longint;
     12   begin
     13     if a>b then exit(b) else exit(a);
     14   end;
     15 
     16 procedure add(x,y,f:longint);
     17   begin
     18     inc(len);
     19     edge[len].point:=y;
     20     edge[len].flow:=f;
     21     edge[len].next:=p[x];
     22     p[x]:=len;
     23   end;
     24 
     25 function sap:longint;
     26   var i,q,tmp,u,j,neck:longint;
     27   begin
     28     neck:=inf;
     29     for i:=0 to t do
     30       cur[i]:=p[i];
     31     numh[0]:=t+1;
     32     u:=0;
     33     sap:=0;
     34     while h[0]<t+1 do
     35     begin
     36       d[u]:=neck;
     37       i:=cur[u];
     38       while i<>-1 do
     39       begin
     40         j:=edge[i].point;
     41         if (edge[i].flow>0) and (h[u]=h[j]+1) then
     42         begin
     43           pre[j]:=u;
     44           cur[u]:=i;
     45           neck:=min(neck,edge[i].flow);
     46           u:=j;
     47           if u=t then
     48           begin
     49             sap:=sap+neck;
     50             while u<>0 do
     51             begin
     52               u:=pre[u];
     53               j:=cur[u];
     54               dec(edge[j].flow,neck);
     55               inc(edge[j xor 1].flow,neck);
     56             end;
     57             neck:=inf;
     58           end;
     59           break;
     60         end;
     61         i:=edge[i].next;
     62       end;
     63       if i=-1 then
     64       begin
     65         dec(numh[h[u]]);
     66         if numh[h[u]]=0 then exit;
     67         tmp:=t;
     68         q:=-1;
     69         i:=p[u];
     70         while i<>-1 do
     71         begin
     72           j:=edge[i].point;
     73           if edge[i].flow>0 then
     74             if h[j]<tmp then
     75             begin
     76               tmp:=h[j];
     77               q:=i;
     78             end;
     79           i:=edge[i].next;
     80         end;
     81         cur[u]:=q;
     82         h[u]:=tmp+1;
     83         inc(numh[h[u]]);
     84         if u<>0 then
     85         begin
     86           u:=pre[u];
     87           neck:=d[u];
     88         end;
     89       end;
     90     end;
     91   end;
     92 
     93 begin
     94   len:=-1;
     95   fillchar(p,sizeof(p),255);
     96   readln(n);
     97   t:=n+1;
     98   for i:=1 to n do
     99   begin
    100     read(s);
    101     for j:=1 to s do
    102     begin
    103       read(x);
    104       add(i,x,inf);
    105       add(x,i,0);
    106       inc(c[x]);
    107       dec(c[i]);
    108     end;
    109   end;
    110   for i:=1 to n do
    111     if c[i]>0 then
    112     begin
    113       m:=m+c[i];
    114       add(0,i,c[i]);
    115       add(i,0,0);
    116     end
    117     else if c[i]<0 then
    118     begin
    119       add(i,t,-c[i]);
    120       add(t,i,0);
    121     end;
    122 
    123   writeln(m-sap);  //一定有解的时候只用跑一次,想想看为什么
    124 end.
    标算
  • 相关阅读:
    Android平台Qt开发入门教程 狼人:
    PySide教程:第一个PySide应用 狼人:
    MeeGo系统1.2版本新组件 狼人:
    讨论:.NET 4各项技术的应用前景,徐汇区网站设计 狼人:
    为 NokiaQt SDK增加新的Symbian SDK开发平台 狼人:
    Skia引擎API整理介绍(skia in Android 2.3 trunk) 狼人:
    Windows Phone 7 开发之:工具栏 狼人:
    Google Adsense(Google网站联盟)广告申请指南 狼人:
    PySide教程:一个简单的点击按钮示例 狼人:
    内部类类4线程两加两减
  • 原文地址:https://www.cnblogs.com/phile/p/4473071.html
Copyright © 2020-2023  润新知