http://blog.sina.com.cn/s/blog_60a0e97e0101bfj9.html
Push-Relabel算法:
算法步骤:
- 初始化前置流:将与源点s相连的管道流量f(0,i)设为该管道的容量,即 f(0,i)=c(0,i);将源点s的高度h(0)=V,(V表示图的顶点个数),其余顶点高度h(i)=0;将源的点余量e(0)设为源容量减去源的流出量,即e(0)=-∑f(0,i)=-∑c(0,i),与源s相连的点余量设为该点的流入量e(i)=c(0,i),其余点都为0。
- 构造一个存储顶点的队列vlist,用以检查点的压栈。从源点s出发,将与之相连的顶点压入栈。
- 每次从栈中取首个元素,即某一个点,检查其点余量e(i),若不为0,表示要对该点进行操作——重标记或者压入流:检查与该点i全部的相邻点j,若该点比它相邻点的高度大h(i)>h(j)且该管道的容量c(i,j)大于流量f(i,j)时,将该点的余量以最大方式压入该管道delta=min(e(i), c(i,j)-f(i,j)), 点余量e、流量f相应的进行减加,另外在队列中加入满足点余量e(j)>0的相邻点j(vlist.push(j); j原不存在该队列中);若没有相邻点满足上述条件,则将该点的高度值h(i)根据相邻点j进行增加,h(i)=min(h(j))+1。以上的重标记或压入流操作循环进行,直至该点的余量e(i)为0。
- 重复第3步,直至队列vlist中没有元素,停止算法,最后输出汇点t的余量e(t),t=V-1, 该值就是最后所求的最大流。最小割就是选择以上
扩展:
- Push-Relabel算法中的顶点i的最大增加量为2V-1,(数学上可以证明),因此到最后可能与源相邻的某个点i会以高度h(i)>V的方式将多余量返回给源(源的高度值始终是V);
- Push-Relabel算法的复杂度为O(V2E)
添加一个示例代码:
http://www.cppblog.com/Icyflame/archive/2009/06/24/88448.html
<pre name="code" class="cpp">#include<iostream>
using namespace std;
#define MAXN 202
int s, t;
int n, np, nc, m;
char str[50];
int c[MAXN][MAXN];
int f[MAXN][MAXN];
int e[MAXN];
int h[MAXN];
void push(int u, int v)
{
int d = min(e[u], c[u][v] - f[u][v]);
f[u][v] += d;
f[v][u] = -f[u][v];
e[u] -= d;
e[v] += d;
}
bool relabel(int u)
{
int mh = INT_MAX;
for(int i=0; i<n+2; i++)
{
if(c[u][i] > f[u][i])
mh = min(mh, h[i]);
}
if(mh == INT_MAX)
return false; //残留网络中无从u出发的路
h[u] = mh + 1;
for(int i=0; i<n+2; i++)
{
if(e[u] == 0) //已无余流,不需再次push
break;
if(h[i] == mh && c[u][i] > f[u][i]) //push的条件
push(u, i);
}
return true;
}
void init_preflow()
{
memset(h, 0, sizeof(h));
memset(e, 0, sizeof(e));
h[s] = n+2;
for(int i=0; i<n+2; i++)
{
if(c[s][i] == 0)
continue;
f[s][i] = c[s][i];
f[i][s] = -f[s][i];
e[i] = c[s][i];
e[s] -= c[s][i];
}
}
void push_relabel()
{
init_preflow();
bool flag = true; //表示是否还有relabel操作
while(flag)
{
flag = false;
for(int i=0; i<n; i++)
if(e[i] > 0)
flag = flag || relabel(i);
}
}
int main()
{
while(scanf("%d%d%d%d", &n, &np, &nc, &m) != EOF)
{
s = n; t = n+1;
memset(c, 0, sizeof(c));
memset(f, 0, sizeof(f));
while(m--)
{
scanf("%s", &str);
int u=0, v=0, z=0;
sscanf(str, "(%d,%d)%d", &u, &v, &z);
c[u][v] = z;
}
for(int i=0; i<np+nc; i++)
{
scanf("%s", &str);
int u=0, z=0;
sscanf(str, "(%d)%d", &u, &z);
if(i < np)
c[s][u] = z;
else if(i >= np && i < np + nc)
c[u][t] = z;
}
push_relabel();
printf("%d\n", e[t]);
}
}