传说中效率最高的最大流算法(Dinic)
呵呵,又从DK那偷代码了,好兴奋哈,以下是这个算法的简单介绍,不过我用它去解决HDU的1532 竟然TLE,郁闷.到时候再继续问问DK吧...so 烦躁.
哈哈 终于经过大牛的指点 原来本算法是从0开始标号的......
Dinic是个很神奇的网络流算法。它是一个基于“层次图”的时间效率优先的最大流算法。
层次图是什么东西呢?层次,其实就是从源点走到那个点的最短路径长度。于是乎,我们得到一个定理:从源点开始,在层次图中沿着边不管怎么走,经过的路径一定是终点在剩余图中的最短路。(摘自WC2007王欣上论文)注意,这里是要按照层次走。
那么,MPLA(最短路径增值)的一大堆复杂的证明我就略掉了,有兴趣的请自行参阅WC2007王欣上神牛的论文。
首先我们得知道,Dinic的基本算法步骤是,先算出剩余图,然后用剩余图算层次图,然后在层次图里找增广路。不知道你想到没有,这个层次图找增广路的方法,恰恰就是Ford-Fulkerson类算法的时间耗费最大的地方,就是找一个最短的增广路。所以呢,层次图就相当于是一个已经预处理好的增广路标志图。
如何实现Dinic呢?
首先我们必然要判一下有没有能到达终点的路径(判存在增广路与否),在这个过程中我们顺便就把层次图给算出来了(当然不用算完),然后就沿着层次图一层一层地找增广路;找到一条就进行增广(注意在沿着层次图找增广路的时候使用栈的结构,把路径压进栈);增广完了继续找,找不到退栈,然后继续找有没有与这个结点相连的下一层结点,直到栈空。如果用递归实现,这个东西就很好办了,不过我下面提供的程序是用了模拟栈,当然这样就不存在结点数过多爆栈的问题了……不过写起来也麻烦了一些,对于“继续找”这个过程我专门开了一个数组存当前搜索的指针。
上面拉拉杂杂说了一大堆,实际上在我的理解中,层次图就是一个流从高往低走的过程(这玩意儿有点像预流推进的标号法……我觉得),在一条从高往低的路径中,自然有些地方会有分叉;这就是Dinic模拟栈中退栈的精华。这就把BFS的多次搜索给省略了不说,时间效率比所谓的理论复杂度要高得多。
这里有必要说到一点,网络流的时间复杂度都是很悲观的,一般情况下绝对没有可能到达那个复杂度的。
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 ;
}
|
|