残差图的最快数据结构

时间:2011-12-23 10:06:17

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

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

结构节点:

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中的Parents(w)的索引。因此,我得到了我存储的向后边缘,可以执行我的算法。然而,这些地图具有日志(E)访问时间,这使我的算法减慢太多。我该如何处理这个问题(双链表实现为std :: vector)?

2 个答案:

答案 0 :(得分:4)

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,反之亦然,但为此,边的起始索引应为零

答案 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”中,我保持所有边缘。在数组中,首先[v]我保持数组e中第一条边的索引离开v。如果数组e中的索引idx中存在边,则反向边存储在索引为idx ^ 1的元素中。 因此,这种表示使我们都能够有一个邻居列表(从第一个[v]开始并跟随下一个索引直到它变为-1)并且能够在恒定时间内访问反向边缘。