• bzoj2324


    出题人真 口袋迷

    很容易发现这是一道费用流的题目

    很显然这个问题有两个难点:

    1. 保证走到某个点时之前序号的点都被走过

    2. 保证每个点都走

    对于1,我们换个说法,一个人走到该点时经过的点的序号都小于该点----->3

    只要满足了2和3,就一定满足1

    现在来看3,也就是说两个点之间的最短路i,j,必须由k(k<j) 来更新

    很像是floyd,事实上我们只要改一改floyd就能满足3

    下面的问题就是,怎么满足2,也就是称之为有下界的网络流

    这里学习了一个非常厉害的方法解决此类问题:

    加无穷小边连接汇点

    对于这道题,我们把除了0号点的以外点拆成两个点,之间连两条边

    一条流量为1,费用为负无穷;一条流量为无穷,费用为0

    什么意思呢?第二条边好理解,表示每个点都可以被经过无数次

    第一条边什么意思呢?保证每个点都被走过;

    因为这条边的费用为极小边,根据费用流的算法,这条边一定会被走过,等价于这个点一定会被走过;

    最后计算总费用的时候我们再把负费用弄掉即可

    后记:

    话说,我在省选的前几天做了这道题目,说了都是泪啊;

    考试的时候D2 T1同样也是有下界的网络流,比这题还简单,

    这不过是边的容量下界是1,(一样的道理,在每条边的基础上加一条流为1,费用为这条边的费用-极小量即可)

    我也很好的写出了处理有下界的网络流的方法;

    可我偏偏脑抽的用到了邻接矩阵,这样就毫无意外的被重边卡死了T T

    自作孽,不可活…………

    UPD:注意这个程序虽然能过但有点问题,具体见后续

      1 const inf=100000007;
      2       bi=10000;
      3 type node=record
      4        from,point,cost,flow,next:longint;
      5      end;
      6 
      7 var edge:array[0..200010] of node;
      8     q:array[0..200010] of longint;
      9     a:array[0..400,0..400] of longint;
     10     p,pre,d:array[0..700] of longint;
     11     v:array[0..700] of boolean;
     12     len,x,y,z,ans,n,m,k,i,j,w,t:longint;
     13 
     14 procedure add(x,y,f,w:longint);
     15   begin
     16     inc(len);
     17     edge[len].from:=x;
     18     edge[len].point:=y;
     19     edge[len].flow:=f;
     20     edge[len].cost:=w;
     21     edge[len].next:=p[x];
     22     p[x]:=len;
     23   end;
     24 
     25 function spfa:boolean;              //网络流基本模板
     26   var i,f,r,x,y:longint;
     27   begin
     28     fillchar(v,sizeof(v),false);
     29     for i:=1 to t do
     30       d[i]:=inf;
     31     d[0]:=0;
     32     f:=1;
     33     r:=1;
     34     q[1]:=0;
     35     v[0]:=true;
     36     while f<=r do
     37     begin
     38       x:=q[f];
     39       v[x]:=false;
     40       i:=p[x];
     41       while i<>-1 do
     42       begin
     43         y:=edge[i].point;
     44         if edge[i].flow>0 then
     45         begin
     46           if d[y]>d[x]+edge[i].cost then
     47           begin
     48             d[y]:=d[x]+edge[i].cost;
     49             pre[y]:=i;
     50             if not v[y] then
     51             begin
     52               v[y]:=true;
     53               inc(r);
     54               q[r]:=y;
     55             end;
     56           end;
     57         end;
     58         i:=edge[i].next;
     59       end;
     60       inc(f);
     61     end;
     62     if d[t]=inf then exit(false) else exit(true);
     63   end;
     64 
     65 procedure mincost;                  //费用流
     66   var i,j:longint;
     67   begin
     68     while spfa do
     69     begin
     70       i:=t;
     71       while i<>0 do
     72       begin
     73         j:=pre[i];
     74         dec(edge[j].flow);           //每次只会流一个人
     75         inc(edge[j xor 1].flow);
     76         i:=edge[j].from;
     77       end;
     78       ans:=ans+d[t];
     79     end;
     80   end;
     81 
     82 begin
     83   readln(n,m,w);
     84   inc(n);
     85   len:=-1;
     86   fillchar(p,sizeof(p),255);
     87   for i:=1 to n do
     88   begin
     89     for j:=1 to n do
     90       a[i,j]:=inf;
     91     a[i,i]:=0;
     92   end;
     93   t:=2*n+1;
     94   for i:=1 to m do
     95   begin
     96     readln(x,y,z);
     97     inc(x);                //仅仅是为了方便
     98     inc(y);
     99     if a[x,y]>z then
    100     begin
    101       a[x,y]:=z;
    102       a[y,x]:=z;
    103     end;
    104   end;
    105   add(0,1,w,0);            //一共有w个人,自然流量为w
    106   add(1,0,0,0);
    107   for i:=2 to n do
    108   begin
    109     add(i,i+n,1,-bi);     //处理下界网络流简单快捷的方法
    110     add(i+n,i,0,bi);
    111     add(i+n,t,1,0);       //每个人都可以停在当前点不走,流直接流出
    112     add(t,n+i,0,0);
    113   end;
    114   for k:=1 to n do
    115   begin
    116     for i:=1 to n do
    117       if i<>k then
    118         for j:=1 to n do
    119           if (i<>j) and (j<>k) then
    120             a[i,j]:=min(a[i,j],a[i,k]+a[k,j]);
    121     if k<>1 then
    122     begin
    123       add(1,k,1,a[1,k]);         
    124       add(k,1,0,-a[1,k]);
    125     end;
    126     for i:=2 to k-1 do         // 边做边建图,显然这时候,i到k的最短路径一定是由编号小于k的点更新来的   
    127     begin
    128       add(i+n,k,1,a[i,k]);     //这个地方和上面流量为什么是1呢?点i到下一个点j可行最小路径只要走一次,
    129                             如果这条路径实际需要走多次的话,那必然有点i到点k的可行最短路径由ij可行最短路更新的来
    130                             于是下一次找增广路的时候,我们直接走ik之间最短路即可
    131                             所以这里我们把每条路径流量限制为1,这样寻找增广路的速度会更快
    132                             (当然像考试时拿不准还是写成inf吧 
    133       add(k,i+n,0,-a[i,k]);
    134     end;
    135   end;
    136   mincost;
    137   writeln(ans+(n-1)*bi);    //修正ans
    138 end.
    View Code
  • 相关阅读:
    Scratch编程:打猎(十)
    Scratch编程:漂亮的时钟(九)
    剑指offer总结一:字符、数字重复问题
    剑指offer:数值的整数次方
    剑指offer:二进制中1的个数
    InnoDB存储引擎与MyIsam存储引擎的区别
    数据库查询慢的原因
    剑指offer:矩形覆盖
    剑指offer:跳台阶问题
    redis键的过期和内存淘汰策略
  • 原文地址:https://www.cnblogs.com/phile/p/4473252.html
Copyright © 2020-2023  润新知