最大流的非递归Dinic算法

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

算法的实现步骤:

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

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

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


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

 

ContractedBlock.gif ExpandedBlockStart.gif 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的方式查找增广路。他是从原点向汇点找,刚开始找到的边(符合条件的边)可能就不在增广路上,可是却要找到头才知道不是增广路,然后在一步步往回退,这岂不是做了大量的无用功?!感觉这样太盲目了~于是想了想:为什么不从汇点向原点找?从汇点向原点不是盲目的,刚开始找到的边(符合条件的边)一定在增广路上!这样就不会做像从原点到汇点找一样做许多无用功!!两段代码都差不多,具体细节看代码:

 

ContractedBlock.gif ExpandedBlockStart.gif 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;
            }
        }
    }
}

 

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

转载于:https://www.cnblogs.com/fornever/archive/2011/09/20/2182903.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值