_Never_

I walk slowly,but I will never stop.

导航

最大流的非递归Dinic算法

Dinic是在Edmond_Karp上的优化,就是传说中的分层;分层听起来难理解,其实就是分级即给节点具有一定规则的标记,看一下实现就懂了!

算法的实现步骤:

1.分层:利用BFS搜索方式给每个节点给予标记level[i];

2.判断分层是否成功即汇点是否被分到级别level[sink]!=0;

3.在分层的基础上即level[i]上寻找所有的增广路、累计流量,回到步骤1;


求最大流的非递归Dinic算法code:

 

View Code
//--------Dinic的非递归正向实现---------
#define NOT(x) (x&1?x+1:x-1)

struct Edge
{
int u;
int value;
int next;
}edge[MAXM
*2];
int level[MAXN],queue[MAXN],node[MAXN],variednode[MAXN];//level给每一个节点分级,即分层;queue分层时当队列用,找增广路时
int front,rear;//当栈用;node[i]为结点i的指针;variednode可变的结点指针(原本是node的复制,但是他不断变化);front,rear
int index;//即队列的首尾;top即栈顶;index即作为edge的下标;
int top;

void Build_Graph(int m)//建图即建立网络
{
int v,u,value;
index
=0; memset(node,-1,sizeof(node));
for(int i=0;i<m;i++)
{
scanf(
"%d %d %d",&v,&u,&value);
++index; //这里用数组模拟指针,模拟链表结构
edge[index].u=u; //重在自己慢慢体会,讲不出来效果的……
edge[index].value=value;
edge[index].next
=node[v];
node[v]
=index;/////建立原网络

++index;
edge[index].u
=v;
edge[index].value
=0;
edge[index].next
=node[u];
node[u]
=index;/////建立反网络
}
}

int Dinic(int source,int sink,int n)
{
int maxflow=0;
int v,u,value;
while(true)
{
memset(level,
0,sizeof(level));
front
=rear=0; level[source]=1;
queue[
0]=source;
while(front<=rear)/////传说中的分层
{
v
=queue[front++];//注意这里的queue当队列用储存的是结点
for(int i=node[v];i!=-1;i=edge[i].next)
{
u
=edge[i].u; value=edge[i].value;
if(value && level[u]==0)
{
level[u]
=level[v]+1;
queue[
++rear]=u;
if(u==sink) break;
}
}
if(u==sink) break;
}

if(level[sink]==0) break;//这个就是判断是否存在增广路,没有就结束了

for(int i=1;i<=n;i++) variednode[i]=node[i];//看variednode——node的复制,以后就不断变化。这就是优化,记录下次开始访问的边。
edge[0].u=source; top=0; queue[++top]=0;//这里的所做是为了“凑”下边的循环,这里的queue做栈用储存边的下标
while(top)//////求该分层下的最短增广路
{
int i; v=edge[queue[top]].u;
for(i=variednode[v];i!=-1;i=edge[i].next)
{
u
=edge[i].u; value=edge[i].value;
if(value && level[u]==level[v]+1)
{
queue[
++top]=i;
variednode[v]
=edge[i].next;
break;
}
}
if(i==-1) { variednode[v]=-1;top--;continue; }//若该点四周不存在最短增广路,则直接variednode[v]=-1;以防下次做
//多余的查找。top--退一条边,再找

if(u==sink)//找到一条边就判断一下该点是不是sink,不是继续向下找
{
int min=0x7fffffff,flag;
for(i=2;i<=top;i++)
if(min>edge[i].value)
min
=edge[i].value;//找到最大流量
for(i=top;i>1;i--)
{
if(min==edge[queue[i]].value)
flag
=i;//更新该正向路径的各个流量,并标记出第一个流量变为0的边所在的栈位

edge[queue[i]].value
-=min;
edge[NOT(queue[i])].value
+=min;//看看怎么建图的吧 }
top=flag-1;//更新top
maxflow+=min;//更新maxflow
}
}
}

return maxflow;
}

这啥玩意缩进啊,草!气死我了,整了半天还这样……


上边的代码,主要是第三步,我想了想感觉有可能会做太多的无用功——在level[i]的基础上找增广路,他是用DFS的方式查找增广路。他是从原点向汇点找,刚开始找到的边(符合条件的边)可能就不在增广路上,可是却要找到头才知道不是增广路,然后在一步步往回退,这岂不是做了大量的无用功?!感觉这样太盲目了~于是想了想:为什么不从汇点向原点找?从汇点向原点不是盲目的,刚开始找到的边(符合条件的边)一定在增广路上!这样就不会做像从原点到汇点找一样做许多无用功!!两段代码都差不多,具体细节看代码:

 

View Code
//------Dinic的非递归的反向实现------
#define NOT(x) ( (x&1)?(x+1):(x-1) ) //取反边

struct Edge
{
   
int u;
   
int value;
   
int next;
}edge[MAXM];
int node[MAXN],variednode[MAXN];
int level[MAXN],queue[MAXN];
int front,rear,top,index;

void Build_Graph(int m)
{
   
int v,u,value;
    memset(node,
-1,sizeof(node)); index=0;
   
for(int i=0;i<m;i++)
    {
        scanf(
"%d %d %d",&v,&u,&value);

        index
++;
        edge[i].u
=u;
        edge[i].value
=value;
        edge[i].next
=node[v];
        node[v]
=index;

        index
++;
        edge[i].u
=v;
        edge[i].value
=0;
        edge[i].next
=node[u];
        node[u]
=index;
    }   
}

int Dinic(int source,int sink,int n)
{
   
int max_flow=0;
   
int i,v,u,value;
   
while(true)
    {
        front
=rear=0;
        queue[rear]
=source;
        memset(level,
0,sizeof(level)); level[source]=1;
       
while(front>=rear)
        {
            v
=queue[front++];
           
for(i=node[v];i!=-1;i=edge[i].next)
            {
                u
=edge[i].u; value=edge[i].value;
               
if(value && level[u]==0)
                {
                    level[u]
=level[v]+1;
                    queue[
++rear]=u;
                   
if(u==sink)    break;
                }
               
if(u==sink)    break;
            }
        }
       
if(level[sink]==0)    break;

       
for(i=1;i<=n;i++)    variednode[i]=node[i];

        edge[
0].u=sink; top=0; queue[++top]=0;
       
while(top)
        {
            v
=edge[queue[top]].u;
           
for(i=variednode[v];i!=-1;i=edge[i].next)
            {
                u
=edge[i].u; value=edge[NOT(i)].value;
               
if(value && level[v]=level[u]+1)
                {
                    variednode[v]
=edge[i].next;
                    queue[
++top]=NOT(i);
                   
break;
                }
            }
           
if(i==-1)
            {
                variednode[v]
=-1;
                top
--;    continue;
            }

           
if(u==source)
            {
               
int min=0x7fffffff,flag;
               
for(i=2;i<=top;i++)
                {
                   
if(min>edge[queue[top]].value)
                        min
=edge[queue[top]].value;
                }
               
for(i=top;i>1;i--)
                {
                   
if(edge[queue[top]].value==min)
                        flag
=i;
                    edge[queue[top]].value
-=min;
                    edge[NOT(queue[top])].value
+=min;
                }
                top
=flag;
                max_flow
+=min;
            }
        }
    }
}

 

我感觉这样反着搜索比较好,可是是否会存在一些漏洞?不知道,至少我现在没发现;如果大虾你发现有什么不对,望能给我指正!十分感谢!!

posted on 2011-09-20 22:17  _Never_  阅读(2160)  评论(3编辑  收藏  举报