这个Sedgewick代码是否正确?

时间:2014-12-01 01:33:38

标签: c++ network-flow

我正在解决一个优化问题,除此之外,我必须最大化流网络。我实现了一个基于c ++代码的流量最大化算法,该算法基于以下 java代码,出现在Sedgewick“Java中的算法,第三版,第5部分:图算法”一书中,它最大化了网络流程使用基于顶点的PREFLOW-push算法:

class NetworkMaxFlow
{ private Network G; private int s, t;
  private int[] h, wt;
  private void initheights()
  NetworkMaxFlow(Network G, int s, int t)
  { this.G = G; this.s = s; this.t = t;
    wt = new int[G.V()]; h = new int[G.V()];
    initheights();
    intGQ gQ = new intGQ(G.V());
    gQ.put(s); wt[t] = -(wt[s] = Edge.M*G.V());
    while (!gQ.empty())
    { int v = gQ.get();
      AdjList A = G.getAdjList(v);
      for (Edge e = A.beg(); !A.end(); e = A.nxt())
        { int w = e.other(v), cap = e.capRto(w);
          int P = cap < wt[v] ? cap : wt[v];
          if (P > 0 && v == s || h[v] == h[w]+1) // first observation (see below)
            { e.addflowRto(w, P);
              wt[v] -= P; wt[w] += P;
              if ((w != s) && (w != t)) gQ.put(w); // enqueue w if it is not source or sink
            }
        }
      if (v != s && v != t && wt[v] > 0) // why check v != t if t never enter the queue?
        { h[v]++; gQ.put(v); }
    }
  }
}

基于该代码,我的实现无法最大化以下网络 Capacitated network; all flows are in zero 执行后,得到的流程如下 Final state for the previous net 通过该流程,流量值为8,但最大值为9,如下图所示 A maximum flow 根据我的理解,该算法与本书的解释是一致的。但是,我看到了两件奇怪的事情

  1. 来源没有明确的预流阶段。它包含在while中并且在谓词P > 0 && v == s为真时仅执行一次且仅执行一次。也许这是为了缩短代码
  2. 根据我的理解和本书的话语,接收器永远不会进入队列。但是,当高度增加时,代码会检查v!= t。有什么理由吗?
  3. 这是我在C ++中实现此算法的摘录

    template <class Net, class Q_Type> typename Net::Flow_Type
    generic_preflow_vertex_push_maximum_flow(Net & net)
    {
      init_height_in_nodes(net); // breadth first traverse from sink to
                     // source. Nodes are labeled with their
                     // minimal distance (in nodes) to sink
      auto source = net.get_source();
      auto sink   = net.get_sink();
    
      using Itor = __Net_Iterator<Net>;
      Q_Type q; // generic queue (can be fifo, heap or random) of active nodes
    
      // preflow: floods all nodes connected to the source 
      for (Itor it(source); it.has_curr(); it.next()) 
        {
          auto arc  = it.get_curr();  
          arc->flow = arc->cap; // saturate arc to its maximum 
          auto tgt = net.get_tgt_node(arc);
          put_in_active_queue(q, tgt);
          assert(node_height<Net>(source) == node_height<Net>(tgt) + 1);
          assert(not is_residual<Net>(source, arc));
        }
    
      while (not q.is_empty()) // while there are active nodes
        {
          auto src = get_from_active_queue(q);
          auto excess = net.get_in_flow(src) - net.get_out_flow(src);
    
          for (Itor it(src); it.has_curr(); it.next()) 
            {
              auto arc = it.get_curr();
              auto tgt = net.get_connected_node(arc, src);
    
              if (node_height<Net>(src) != node_height<Net>(tgt) + 1)
                continue; // this arc is not eligible
    
              typename Net::Flow_Type flow_to_push;
              if (is_residual<Net>(src, arc))
                {
                  flow_to_push = std::min(arc->flow, excess);
                  arc->flow -= flow_to_push;
                }
              else
                {
                  flow_to_push = std::min(arc->cap - arc->flow, excess);
                  arc->flow += flow_to_push;
                }
    
              excess -= flow_to_push;
              if (tgt != sink and tgt != source)
                put_in_active_queue(q, tgt);
            }
    
        if (excess > 0) // src still active?
          { 
            node_height<Net>(src)++;
            put_in_active_queue(q, src);
          }
      }
    
      return net.flow_value(); // sum of all outing flow from source
    }
    

    ¿有人发现我的代码和Sedgewick的代码之间存在任何逻辑上的不一致吗?我的印象是我的代码(也许还有Sedgewick)没有正确处理高度的增加。但我无法理解为什么

    我显示了一个详细的执行跟踪,网络无法最大化(跟踪从第一个q.get()开始。括号中的值是高度的值.IN是到节点的传入流出来的那个。

    例如,行

        4104 (2) --> 0 (1) pushing 1 from 4104 toward 0
    

    指的是合格弧4104 - > 0。节点4104具有高度2并且节点0具有高度1.表达“推动1”意味着将1个统一的流推向目标节点(0)。行================分隔每个队列提取。队列是FIFO,并且在每次处理结束时打印其状态。

    请注意,很多时候推送或减少零流量单位,但目标节点变为活动状态。

    这是执行追踪

    Initial Queue = 4104 4105 4106 4107 4108
    
    Active node 4104 Height = 2 IN = 1 OUT = 0
        4104 (2) --> source (3) not eligible
        4104 (2) --> 0 (1) pushing 1 from 4104 toward 0
        4104 (2) --> 1 (1) pushing 0 from 4104 toward 1
        4104 (2) --> 2 (1) pushing 0 from 4104 toward 2
        4104 (2) --> 4 (1) pushing 0 from 4104 toward 4
        Excess = 0
        Queue = 4105 4106 4107 4108 0 1 2 4
    ================
    Active node 4105 Height = 2 IN = 3 OUT = 0
        4105 (2) --> source (3) not eligible
        4105 (2) --> 1 (1) pushing 1 from 4105 toward 1
        4105 (2) --> 4 (1) pushing 1 from 4105 toward 4
        4105 (2) --> 6 (1) pushing 1 from 4105 toward 6
        Excess = 0
        Queue = 4106 4107 4108 0 1 2 4 6
    ================
    Active node 4106 Height = 2 IN = 1 OUT = 0
        4106 (2) --> source (3) not eligible
        4106 (2) --> 1 (1) pushing 1 from 4106 toward 1
        4106 (2) --> 5 (1) pushing 0 from 4106 toward 5
        Excess = 0
        Queue = 4107 4108 0 1 2 4 6 5
    ================
    Active node 4107 Height = 2 IN = 1 OUT = 0
        4107 (2) --> source (3) not eligible
        4107 (2) --> 1 (1) pushing 1 from 4107 toward 1
        4107 (2) --> 2 (1) pushing 0 from 4107 toward 2
        4107 (2) --> 3 (1) pushing 0 from 4107 toward 3
        4107 (2) --> 4 (1) pushing 0 from 4107 toward 4
        4107 (2) --> 6 (1) pushing 0 from 4107 toward 6
        Excess = 0
        Queue = 4108 0 1 2 4 6 5 3
    ================
    Active node 4108 Height = 2 IN = 3 OUT = 0
        4108 (2) --> source (3) not eligible
        4108 (2) --> 1 (1) pushing 1 from 4108 toward 1
        4108 (2) --> 2 (1) pushing 1 from 4108 toward 2
        4108 (2) --> 4 (1) pushing 1 from 4108 toward 4
        4108 (2) --> 5 (1) pushing 0 from 4108 toward 5
        4108 (2) --> 6 (1) pushing 0 from 4108 toward 6
        Excess = 0
        Queue = 0 1 2 4 6 5 3
    ================
    Active node 0 Height = 1 IN = 1 OUT = 0
        0 (1) --> sink (0) pushing 1 from 0 toward sink
        0 (1) --> 4104 (2) not eligible
        Excess = 0
        Queue = 1 2 4 6 5 3
    ================
    Active node 1 Height = 1 IN = 4 OUT = 0
        1 (1) --> sink (0) pushing 2 from 1 toward sink
        1 (1) --> 4105 (2) not eligible
        1 (1) --> 4106 (2) not eligible
        1 (1) --> 4107 (2) not eligible
        1 (1) --> 4108 (2) not eligible
        Excess = 2    1 goes back onto queue with label 2
        Queue = 2 4 6 5 3 1
    ================
    Active node 2 Height = 1 IN = 1 OUT = 0
        2 (1) --> sink (0) pushing 1 from 2 toward sink
        2 (1) --> 4108 (2) not eligible
        Excess = 0
        Queue = 4 6 5 3 1
    ================
    Active node 4 Height = 1 IN = 2 OUT = 0
        4 (1) --> sink (0) pushing 2 from 4 toward sink
        4 (1) --> 4105 (2) not eligible
        4 (1) --> 4108 (2) not eligible
        Excess = 0
        Queue = 6 5 3 1
    ================
    Active node 6 Height = 1 IN = 1 OUT = 0
        6 (1) --> sink (0) pushing 1 from 6 toward sink
        6 (1) --> 4105 (2) not eligible
        Excess = 0
        Queue = 5 3 1
    ================
    Active node 5 Height = 1 IN = 0 OUT = 0
        5 (1) --> sink (0) pushing 0 from 5 toward sink
        Excess = 0
        Queue = 3 1
    ================
    Active node 3 Height = 1 IN = 0 OUT = 0
        3 (1) --> sink (0) pushing 0 from 3 toward sink
        Excess = 0
        Queue = 1
    ================
    Active node 1 Height = 2 IN = 4 OUT = 2
        1 (2) --> 4105 (2) not eligible
        1 (2) --> 4106 (2) not eligible
        1 (2) --> 4107 (2) not eligible
        1 (2) --> 4108 (2) not eligible
        Excess = 2    1 goes back onto queue with label 3
        Queue = 1
    ================
    Active node 1 Height = 3 IN = 4 OUT = 2
        1 (3) --> 4105 (2) Reducing 1 from 1 toward 4105
        1 (3) --> 4106 (2) Reducing 1 from 1 toward 4106
        1 (3) --> 4107 (2) Reducing 0 from 1 toward 4107
        1 (3) --> 4108 (2) Reducing 0 from 1 toward 4108
        Excess = 0
        Queue = 4105 4106 4107 4108
    ================
    Active node 4105 Height = 2 IN = 3 OUT = 2
        4105 (2) --> source (3) not eligible
        4105 (2) --> 1 (3) not eligible
        Excess = 1    4105 goes back onto queue with label 3
        Queue = 4106 4107 4108 4105
    ================
    Active node 4106 Height = 2 IN = 1 OUT = 0
        4106 (2) --> source (3) not eligible
        4106 (2) --> 1 (3) not eligible
        4106 (2) --> 5 (1) pushing 1 from 4106 toward 5
        Excess = 0
        Queue = 4107 4108 4105 5
    ================
    Active node 4107 Height = 2 IN = 1 OUT = 1
        4107 (2) --> source (3) not eligible
        4107 (2) --> 2 (1) pushing 0 from 4107 toward 2
        4107 (2) --> 3 (1) pushing 0 from 4107 toward 3
        4107 (2) --> 4 (1) pushing 0 from 4107 toward 4
        4107 (2) --> 6 (1) pushing 0 from 4107 toward 6
        Excess = 0
        Queue = 4108 4105 5 2 3 4 6
    ================
    Active node 4108 Height = 2 IN = 3 OUT = 3
        4108 (2) --> source (3) not eligible
        4108 (2) --> 5 (1) pushing 0 from 4108 toward 5
        4108 (2) --> 6 (1) pushing 0 from 4108 toward 6
        Excess = 0
        Queue = 4105 5 2 3 4 6
    ================
    Active node 4105 Height = 3 IN = 3 OUT = 2
        4105 (3) --> source (3) not eligible
        4105 (3) --> 1 (3) not eligible
        Excess = 1    4105 goes back onto queue with label 4
        Queue = 5 2 3 4 6 4105
    ================
    Active node 5 Height = 1 IN = 1 OUT = 0
        5 (1) --> sink (0) pushing 1 from 5 toward sink
        5 (1) --> 4106 (2) not eligible
        Excess = 0
        Queue = 2 3 4 6 4105
    ================
    Active node 2 Height = 1 IN = 1 OUT = 1
        2 (1) --> sink (0) pushing 0 from 2 toward sink
        2 (1) --> 4108 (2) not eligible
        Excess = 0
        Queue = 3 4 6 4105
    ================
    Active node 3 Height = 1 IN = 0 OUT = 0
        3 (1) --> sink (0) pushing 0 from 3 toward sink
        Excess = 0
        Queue = 4 6 4105
    ================
    Active node 4 Height = 1 IN = 2 OUT = 2
        4 (1) --> 4105 (4) not eligible
        4 (1) --> 4108 (2) not eligible
        Excess = 0
        Queue = 6 4105
    ================
    Active node 6 Height = 1 IN = 1 OUT = 1
        6 (1) --> sink (0) pushing 0 from 6 toward sink
        6 (1) --> 4105 (4) not eligible
        Excess = 0
        Queue = 4105
    ================
    Active node 4105 Height = 4 IN = 3 OUT = 2
        4105 (4) --> source (3) Reducing 1 from 4105 toward source
        4105 (4) --> 1 (3) pushing 0 from 4105 toward 1
        Excess = 0
        Queue = 1
    ================
    Active node 1 Height = 3 IN = 2 OUT = 2
        1 (3) --> 4107 (2) Reducing 0 from 1 toward 4107
        1 (3) --> 4108 (2) Reducing 0 from 1 toward 4108
        Excess = 0
        Queue = 4107 4108
    ================
    Active node 4107 Height = 2 IN = 1 OUT = 1
        4107 (2) --> source (3) not eligible
        4107 (2) --> 2 (1) pushing 0 from 4107 toward 2
        4107 (2) --> 3 (1) pushing 0 from 4107 toward 3
        4107 (2) --> 4 (1) pushing 0 from 4107 toward 4
        4107 (2) --> 6 (1) pushing 0 from 4107 toward 6
        Excess = 0
        Queue = 4108 2 3 4 6
    ================
    Active node 4108 Height = 2 IN = 3 OUT = 3
        4108 (2) --> source (3) not eligible
        4108 (2) --> 5 (1) pushing 0 from 4108 toward 5
        4108 (2) --> 6 (1) pushing 0 from 4108 toward 6
        Excess = 0
        Queue = 2 3 4 6 5
    ================
    Active node 2 Height = 1 IN = 1 OUT = 1
        2 (1) --> sink (0) pushing 0 from 2 toward sink
        2 (1) --> 4108 (2) not eligible
        Excess = 0
        Queue = 3 4 6 5
    ================
    Active node 3 Height = 1 IN = 0 OUT = 0
        3 (1) --> sink (0) pushing 0 from 3 toward sink
        Excess = 0
        Queue = 4 6 5
    ================
    Active node 4 Height = 1 IN = 2 OUT = 2
        4 (1) --> 4105 (4) not eligible
        4 (1) --> 4108 (2) not eligible
        Excess = 0
        Queue = 6 5
    ================
    Active node 6 Height = 1 IN = 1 OUT = 1
        6 (1) --> sink (0) pushing 0 from 6 toward sink
        6 (1) --> 4105 (4) not eligible
        Excess = 0
        Queue = 5
    ================
    Active node 5 Height = 1 IN = 1 OUT = 1
        5 (1) --> sink (0) pushing 0 from 5 toward sink
        5 (1) --> 4106 (2) not eligible
        Excess = 0
        Queue =
    

1 个答案:

答案 0 :(得分:3)

您的问题

预流

  

source没有明确的预流阶段。它包含在while中,并且在谓词P > 0 && v == strue时仅执行一次且仅执行一次。也许这是为了缩短代码

是的,我认为这样做是为了压缩代码。由于在开始时赋值wt[s] = Edge.M*G.V(),源具有足够的“虚拟”过量,以便在算法的第一次迭代中为预流提供燃料。如果你想在你的实现中做同样的技巧,你将不得不在第一次迭代期间膨胀excess变量(你在飞行中计算而不是将它存储在像Sedgewick这样的数组中),当你遇到源节点。但是你不必这样做,你明确的预装似乎很好,甚至可能使事情更具可读性。

我还怀疑在条件P > 0 && v == s || h[v] == h[w]+1中,隐含的运算符优先级(P > 0 && v == s) || h[v] == h[w]+1不是预期的。编写方式时,仅对源案例执行检查P > 0。在其他情况下不检查它不会有害,因为使用P == 0执行主体只会将0超出其他节点(不执行任何操作),并且不必要地将其他节点添加到队列中,仅用于他们立即被删除。我见过你以同样的方式实现它。即使稍微浪费计算时间,也没关系。可能P > 0 && (v == s || h[v] == h[w]+1)确实是有意的。

汇入队列

  

根据我的理解和本书的话语,接收器永远不会进入队列。但是,当高度增加时,代码会检查v!= t。有什么理由吗?

我同意,这很奇怪。接收器可以进入队列的唯一方法是同时作为源(在只有一个节点且没有边缘的图形中)。但是,在这种情况下,条件v != s已经避免了无限循环,没有必要的额外条件。

错误的结果

  

执行后,得到的流程如下[...]使用此流程,流量值为8,但最大值为9

初始高度

您的结果错误,因为您的初始高度是错误的。我无法将Sedgewick的初始化代码与您的初始代码进行比较,因为您的帖子也没有指定。但是我从你的日志文件中了解到,source的高度为3。这显然是针对the conditions for height functionssource必须从图表中的节点数开始,并在整个算法中保留它。

在您的情况下,source的高度太接近其下游邻居的高度。这导致下游邻居很快将一些流量推回到源,然后再努力让它进一步向下游流动。如果没有办法让它流到接收器,它们应该只用

破碎图?

我还担心边缘[4104 -> 1]似乎消失了。虽然在节点4104的处理期间提到它,但它在节点1的处理期间从不出现。所有其他进入边缘被提及,无论是关于“不合格”,“推”还是“减少”。我希望它看起来像三种类型中的一种,就像其他类型一样。但话说回来,我不知道你把这些日志记录放在哪里。尽管如此,它还是引起了一些担忧,我想我会提到它,以防你在修好高度后仍然遇到麻烦。