【问题标题】:Fastest data structure for residual graphs残差图的最快数据结构
【发布时间】:2012-01-26 17:35:22
【问题描述】:

我正在尝试实现一个包含数千个节点和边的流算法,因此我需要高效的数据结构。目前我做以下事情:

结构节点:

Double Linked Array (Parents) //Edges that enter the node (basicaly a tuple that contains a pointer to the parent node and the weight, current flow of the edge
Double Linked Array (Sons) //Edges that leave the node

问题是,当我执行 BFS 时,给定一个节点 v 我需要查看 residual graph 中的边缘(基本上是您发送流的边缘的后向边缘),留下 v。因为我可以平行边我需要始终知道哪个后向边缘来自哪个前向边缘。

目前我正在通过首先处理 Sons(v) 中的所有边来解决问题,然后我定义了一个映射,它为我提供了所有这些边的目标节点 w 中的父母 (w) 的索引。因此,我得到了我存储的后向边缘并可以执行我的算法。但是,这些地图具有 log(E) 访问时间,这大大降低了我的算法速度。我应该如何解决这个问题(双链表实现为 std::vector)?

【问题讨论】:

    标签: c++ data-structures graph-algorithm


    【解决方案1】:
    int src,snk,nnode,nedge;
    int fin[100000],dist[100000];//nodes
    int cap[100000],next[100000],to[100000];
    void init(int s,int sn,int n)
    {
        src=s,snk=sn,nnode=n,nedge=0;
        memset(fin,-1,sizeof(fin));
    }
    void add(int u,int v,int c)
    {
        to[nedge]=v,cap[nedge]=c,next[nedge]=fin[u],fin[u]=nedge++;
        to[nedge]=u,cap[nedge]=0,next[nedge]=fin[v],fin[v]=nedge++;
    }
    bool bfs()
    {
        int e,u,v;
        memset(dist,-1,sizeof(dist));
        dist[src]=0;
        queue<int> q;
        q.push(src);
        while(!q.empty())
        {
            u=q.front();
            q.pop();
            for(e=fin[u];e>=0;e=next[e])
            {
                v=to[e];
                if(cap[e]>0&&dist[v]==-1)
                {
                    dist[v]=dist[u]+1;
                    q.push(v);
                }
            }
        }
        if(dist[snk]==-1)
            return false;
        else
            return true;
    }
    int dfs(int u,int flow)
    {
        if(u==snk)
            return flow;
        int e,v,df;
        for(e=fin[u];e>=0;e=next[e])
        {
            v=to[e];
            if(cap[e]>0&&dist[v]==dist[u]+1)
            {
                df=dfs(v,min(cap[e],flow));
                if(df>0)
                {
                    cap[e]-=df;
                    cap[e^1]+=df;
                    return df;
                }
            }
        }
        return 0;
    }
    int dinitz()
    {
        int ans=0;
        int df,i;
        while(bfs())
        {
            while(1)
            {
                df=dfs(src,INT_MAX);
                if(df>0)
                    ans+=df;
                else
                    break;
            }
        }
        return ans;
    }
    

    这是我的 dinitz 算法代码 这里init函数初始化邻接表 add 在列表中添加一条新边,fin 给出该邻接列表中的最后一个节点 所以你可以通过以下循环访问列表中的所有元素

    for(e=fin[u];e>=0;e=next[e])
    {
         v=to[e];
    }
    

    其中 u 是要查找其相邻元素的节点 v 将相邻元素给 u 同样在找到最大流量时,您需要前向边缘和后向边缘 所以假设前边缘是e 那么后向边缘将是 e^1 ,反之亦然,但是对于边缘的起始索引应该为零

    【讨论】:

      【解决方案2】:

      我使用的表示类似于边缘列表,但带有附加信息

      typedef long long dintype;
      struct edge{
        edge(int t_ = 0,int n_ = 0, dintype c_ = 0){
          to = t_;
          next = n_;
          cap = c_;
        }
        int to,next;
        dintype cap;
      };
      const int max_edges = 131010;
      const int max_nodes = 16010;
      edge e[max_edges];
      int first[max_nodes]; // initialize this array with -1
      int edges_num;
      inline void add_edge(int from,int to, dintype cap){
        if(edges_num == 0){
          memset(first,-1,sizeof(first));
        }
        e[edges_num].to = to;e[edges_num].cap = cap;
        e[edges_num].next = first[from];first[from] = edges_num++;
      
        e[edges_num].to = from;e[edges_num].cap = 0;
        e[edges_num].next = first[to];first[to] = edges_num++;
      }
      

      我使用全局数组来更好地解释这个想法。我将它用于我的dinitz algorithm

      现在解释一下。在数组“e”中,我持有所有的边。在数组 first[v] 中,我保存数组 e 中从 v 出来的第一条边的索引。如果数组 e 的索引 idx 中存在一条边,则反向边存储在索引为 idx^1 的元素中。 因此,这种表示使我们既可以拥有一个邻居列表(从 first[v] 开始,然后跟随下一个索引,直到它变为 -1),并且能够在恒定时间内访问反向边缘。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-17
        相关资源
        最近更新 更多