posts - 195, comments - 34, trackbacks - 0, articles - 1

传说中效率最高的最大流算法(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;
}
« 上一篇:KM算法(转)» 下一篇:字典树



只有注册用户登录后才能发表评论。


网站导航: