传说中效率最高的最大流算法(Dinic)

 

呵呵,又从DK那偷代码了,好兴奋哈,以下是这个算法的简单介绍,不过我用它去解决HDU的1532 竟然TLE,郁闷.到时候再继续问问DK吧...so 烦躁.

 

哈哈 终于经过大牛的指点 原来本算法是从0开始标号的......

 

Dinic是个很神奇的网络流算法。它是一个基于“层次图”的时间效率优先的最大流算法。

层次图是什么东西呢?层次,其实就是从源点走到那个点的最短路径长度。于是乎,我们得到一个定理:从源点开始,在层次图中沿着边不管怎么走,经过的路径一定是终点在剩余图中的最短路。(摘自WC2007王欣上论文)注意,这里是要按照层次走。

那么,MPLA(最短路径增值)的一大堆复杂的证明我就略掉了,有兴趣的请自行参阅WC2007王欣上神牛的论文。

首先我们得知道,Dinic的基本算法步骤是,先算出剩余图,然后用剩余图算层次图,然后在层次图里找增广路。不知道你想到没有,这个层次图找增广路的方法,恰恰就是Ford-Fulkerson类算法的时间耗费最大的地方,就是找一个最短的增广路。所以呢,层次图就相当于是一个已经预处理好的增广路标志图。

如何实现Dinic呢?

首先我们必然要判一下有没有能到达终点的路径(判存在增广路与否),在这个过程中我们顺便就把层次图给算出来了(当然不用算完),然后就沿着层次图一层一层地找增广路;找到一条就进行增广(注意在沿着层次图找增广路的时候使用栈的结构,把路径压进栈);增广完了继续找,找不到退栈,然后继续找有没有与这个结点相连的下一层结点,直到栈空。如果用递归实现,这个东西就很好办了,不过我下面提供的程序是用了模拟栈,当然这样就不存在结点数过多爆栈的问题了……不过写起来也麻烦了一些,对于“继续找”这个过程我专门开了一个数组存当前搜索的指针。

上面拉拉杂杂说了一大堆,实际上在我的理解中,层次图就是一个流从高往低走的过程(这玩意儿有点像预流推进的标号法……我觉得),在一条从高往低的路径中,自然有些地方会有分叉;这就是Dinic模拟栈中退栈的精华。这就把BFS的多次搜索给省略了不说,时间效率比所谓的理论复杂度要高得多。

这里有必要说到一点,网络流的时间复杂度都是很悲观的,一般情况下绝对没有可能到达那个复杂度的。

 

#include < iostream >
using   namespace  std;
const   long  maxn = 300 ;
const   long  maxm = 300000 ;
const   long  inf = 0x7fffffff ;
struct  node
{
    
long  v,next;
    
long  val;
}s[maxm
* 2 ];
long  level[maxn],p[maxn],que[maxn], out [maxn],ind;
void  init()
{
    ind
= 0 ;
    memset(p,
- 1 , sizeof (p));
}
inline 
void  insert(long x,long  y,long  z)
{
    s[ind].v
= y;
    s[ind].val
= z;
    s[ind].next
= p[x];
    p[x]
= ind ++ ;
    s[ind].v
= x;
    s[ind].val
= 0 ;
    s[ind].next
= p[y];
    p[y]
= ind ++ ;
}
inline 
void  insert2(long  x,long  y,long  z)
{
    s[ind].v
= y;
    s[ind].val
= z;
    s[ind].next
= p[x];
    p[x]
= ind ++ ;
    s[ind].v
= x;
    s[ind].val
= z;
    s[ind].next
= p[y];
    p[y]
= ind ++ ;
}
long  max_flow( long  n, long  source, long  sink)
{
    long
ret = 0 ;
    long
 h = 0 ,r = 0 ;
    
while ( 1 )
    {
        
long  i;
        
for (i = 0 ;i < n; ++ i)
            level[i]
= 0 ;
        h
= 0 ,r = 0 ;
        level[source]
= 1 ;
        que[
0 ] = source;
        
while (h <= r)
        {
            
long  t = que[h ++ ];
            
for (i = p[t];i !=- 1 ;i = s[i].next)
            {
                
if (s[i].val && level[s[i].v] == 0 )
                {
                    level[s[i].v]
= level[t] + 1 ;
                    que[
++ r] = s[i].v;
                }
            }
        }
        
if (level[sink] == 0 ) break ;
        
for (i = 0 ;i < n; ++ i) out [i] = p[i];
        
long  q =- 1 ;
        
while ( 1 )
        {
            
if (q < 0 )
            {
                
long  cur = out [source];
                
for (;cur !=- 1 ;cur = s[cur].next)
                {
                    
if (s[cur].val && out [s[cur].v] !=- 1 && level[s[cur].v] == 2 )
                    {
                        
break ;
                    }
                }
                
if (cur >= 0 )
                {
                    que[
++ q] = cur;
                    
out [source] = s[cur].next;
                }
                
else
                {
                    
break ;
                }
            }
            
long  u = s[que[q]].v;
            
if (u == sink)
            {
                
long  dd = inf;
                
long  index =- 1 ;
                
for ( i = 0 ;i <= q;i ++ )
                {
                    
if (dd > s[que[i]].val)
                    {
                        dd
= s[que[i]].val;
                        index
= i;
                    }
                }
                ret
+= dd;
                
// cout<<ret<<endl;
                 for (i = 0 ;i <= q;i ++ )
                {
                    s[que[i]].val
-= dd;
                    s[que[i]
^ 1 ].val += dd;    
                }
                
for (i = 0 ;i <= q;i ++ )
                {
                    
if (s[que[i]].val == 0 )
                    {
                        q
= index - 1 ;
                        
break ;
                    }
                }
            }
            
else
            {
                
long  cur = out [u];
                
for (;cur !=- 1 ;cur = s[cur].next)
                {
                    
if (s[cur].val && out [s[cur].v] !=- 1 && level[u] + 1 == level[s[cur].v])
                    {
                        
break ;
                    }
                }
                
if (cur !=- 1 )
                {
                    que[
++ q] = cur;
                    
out [u] = s[cur].next;
                }
                
else
                {
                    
out [u] =- 1 ;
                    q
-- ;
                }
            }
        }
    }
    
return  ret;
}

long  m,n;

int  main()
{

    
while (scanf( " %ld %ld " , & m, & n) != EOF)
    {
        init();
        
for ( int  i = 0 ;i < n;i ++ )
        {
            
long  from,to,cost;
            scanf(
" %ld %ld %ld " , & from, & to, & cost);
            insert(--from,--to,cost);
        }
        
long  Start,End;
        scanf(
" %ld %ld " , & Start, & End);
        printf(
" %ld\n " ,max_flow(n,--Start,--End));
    }
    
return   0 ;
}

转载于:https://www.cnblogs.com/zhuangli/archive/2008/08/04/1259527.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值