zoukankan      html  css  js  c++  java
  • bzoj1930

    一开始我觉得这不是一个弱弱的费用流吗?

    每个豆豆拆点,入点出点随便连连

    由于肯定是DAG图,边权为正的最大费用肯定能增广出来

    于是我们只要跑总流量为2的最大费用最大流不就行了吗

    但是

    这样会TLE,因为会出现稠密图,spfa跑稠密图太慢

    既然这样,能不能换一个找增广路的方法呢?

    最大路径更快的方法就是DAG图的拓扑排序+dp

    但好像无法处理反向弧导致带来的环啊

    那既然这样,反正总流量很小,

    我们可以用DAG解决最大路径特有的dp找第一条增广路(因为这时候是肯定的DAG),跑出一个较大费用

    然后再用spfa增广呢?这样就大大减小了时间复杂度

    这就是AC方法

    注意这道题空间卡的较紧,能开integer不要开longint

    实现起来要小心

      1 const mo=4010;
      2 type node=record
      3        next:longint;
      4        point,flow,cost:integer;
      5      end;
      6 
      7 var edge:array[0..4100000] of node;
      8     v:array[0..4010] of boolean;
      9     d,rd,pre,pref:array[0..4010] of integer;
     10     q:array[0..4010] of integer;
     11     p,x,y,cur:array[0..4010] of longint;
     12     len:longint;
     13     max,s,t,i,j,n,m,be:integer;
     14 
     15 procedure add(x,y,f,c:integer);
     16   begin
     17     edge[len].point:=y;
     18     edge[len].flow:=f;
     19     edge[len].cost:=c;
     20     edge[len].next:=p[x];
     21     p[x]:=len;
     22   end;
     23 
     24 procedure preflow;
     25   var x,y,f,r,i,j:longint;
     26   begin
     27     r:=1;
     28     f:=1;
     29     q[1]:=s;
     30     while f<=r do  //拓扑排序
     31     begin
     32       x:=q[f];
     33       i:=p[x];
     34       while i<>-1 do
     35       begin
     36         y:=edge[i].point;
     37         dec(rd[y]);
     38         if (rd[y]=0) then
     39         begin
     40           inc(r);
     41           q[r]:=y;
     42         end;
     43         i:=edge[i].next;
     44       end;
     45       inc(f);
     46     end;
     47     fillchar(d,sizeof(d),255);
     48     d[s]:=0;
     49     for j:=1 to r do //DAG图根据拓扑的顺序dp解决最大路径
     50     begin
     51       x:=q[j];
     52       i:=p[x];
     53       while i<>-1 do
     54       begin
     55         y:=edge[i].point;
     56         if d[y]<d[x]+1 then
     57         begin
     58           d[y]:=d[x]+1;
     59           pre[y]:=x;
     60           cur[y]:=i;
     61           if d[y]>d[max] then max:=y;
     62         end;
     63         i:=edge[i].next;
     64       end;
     65     end;
     66   end;
     67 
     68 procedure change;   //建立预处理后的网络继续增广
     69   var i,j,e,w:longint;
     70   begin
     71     for i:=1 to n do
     72       pref[i]:=1;
     73     i:=max;
     74     while i<>s do   //相当于找到第一条增广路然后弄一弄反向弧什么的
     75     begin
     76       pref[i]:=0;
     77       j:=cur[i];
     78       dec(edge[j].flow);
     79       if pre[i]=s then w:=i;
     80       i:=pre[i];
     81     end;
     82     len:=-1;
     83     for i:=1 to n do
     84       for j:=1 to n do
     85         if (i<>j) then
     86           if (x[i]<=x[j]) and (y[i]<=y[j]) then
     87           begin
     88             inc(len);
     89             edge[len].point:=j+n;
     90             inc(len);
     91             add(j+n,i,1-edge[len-1].flow,0)  //添加原来没加的反向弧
     92           end;
     93 
     94     p[s]:=-1;
     95     for i:=1 to n do   //下面很多处理的细节,不赘述,繁杂但不难想到
     96     begin
     97       if i=w then e:=0 else e:=1;
     98       inc(len);
     99       add(s,i+n,e,0);
    100       inc(len);
    101       add(i+n,s,1-e,0);
    102     end;
    103 //0 表示超级源点,s表示起点,t表示超级汇点
    104     t:=2*n+2;
    105     inc(len);
    106     add(0,s,1,0);
    107     inc(len);
    108     add(s,0,1,0);
    109     for i:=1 to n do   
    110     begin
    111       inc(len);
    112       add(i+n,i,pref[i],1);
    113       inc(len);
    114       add(i,i+n,1-pref[i],-1);
    115       if max=i then e:=0   
    116       else e:=1;
    117       inc(len);
    118       add(i,t,e,0);
    119       inc(len);
    120       add(t,i,1-e,0);
    121     end;
    122   end;
    123 
    124 function spfa:boolean;
    125   var f,r,x,y,i,j,head,tail:longint;
    126   begin
    127     fillchar(v,sizeof(v),false);
    128     for i:=0 to t do
    129       d[i]:=-mo;
    130     d[0]:=0;
    131     q[0]:=0;
    132     f:=0;
    133     r:=0;
    134     head:=1;
    135     tail:=1;
    136     while head<=tail do  //缩空间的写法,好久没这么写了
    137     begin
    138       x:=q[f];
    139       v[x]:=false;
    140       i:=p[x];
    141       while i<>-1 do
    142       begin
    143         y:=edge[i].point;
    144         if edge[i].flow>0 then
    145         begin
    146           if d[x]+edge[i].cost>d[y] then
    147           begin
    148             d[y]:=edge[i].cost+d[x];
    149             pre[y]:=x;
    150             cur[y]:=i;
    151             if not v[y] then
    152             begin
    153               v[y]:=true;
    154               r:=(r+1) mod mo;
    155               inc(tail);
    156               q[r]:=y;
    157             end;
    158           end;
    159         end;
    160         i:=edge[i].next;
    161       end;
    162       f:=(f+1) mod mo;
    163       inc(head);
    164     end;
    165     if d[t]=-mo then exit(false) else exit(true);
    166   end;
    167 
    168 function mincost:longint;  //最大费用最大流
    169   var i,j:longint;
    170   begin
    171     be:=d[max];
    172     mincost:=be;   //预处理出来的费用
    173     while spfa do
    174     begin
    175       mincost:=mincost+d[t];
    176       i:=t;
    177       while i<>0 do
    178       begin
    179         j:=cur[i];
    180         dec(edge[j].flow);
    181         inc(edge[j xor 1].flow);
    182         i:=pre[i];
    183       end;
    184     end;
    185   end;
    186 
    187 begin
    188   len:=-2;
    189   fillchar(p,sizeof(p),255);
    190   readln(n);
    191   for i:=1 to n do
    192     readln(x[i],y[i]);
    193   for i:=1 to n do
    194     for j:=1 to n do
    195       if (i<>j) then
    196         if (x[i]<=x[j]) and (y[i]<=y[j]) then
    197         begin
    198           len:=len+2;
    199           add(i,j,1,0);  //个人感觉这样建之后添加边更容易一点
    200           inc(rd[j]);
    201         end;
    202 
    203   s:=2*n+1;
    204   for i:=1 to n do
    205     if rd[i]=0 then
    206     begin
    207       len:=len+2;
    208       add(s,i,1,0);
    209       inc(rd[i]); 
    210     end;
    211 
    212   preflow;
    213   change;
    214   writeln(mincost);
    215 end.
    View Code
  • 相关阅读:
    解决SecureCRT中文显示乱码
    能大大提升工作效率和时间效率的9个重要习惯
    PHP基础知识
    CI学习相关地址
    IE8引入JavaScript
    IE9以下不支持placeholder属性
    IE8浏览器兼容性问题
    简单的DOS命令
    Linux常用运维指令
    log4j日志+面向切面监控异常
  • 原文地址:https://www.cnblogs.com/phile/p/4473207.html
Copyright © 2011-2022 走看看