呵呵,又从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
;
}