压入与重标记算法
余流
压入和重标记算法(push-relable)引入了一个新的概念叫做余流,余流的定义为e(u)=f(V,u)。
简介
与Ford-Fulkerson方法不同,压入和重标记算法不是检查整个残留网络来找出增广路径,而是每次仅对一个顶点进行操作,并且仅检查残留网络中该顶点的相邻顶点。压入和重标记算法引入了一个新的概念叫做余流,余流的定义为e(u)=f(V,u)。我们知道,在流网络满足三个限制条件的情况下有e(u)=0,但是在该算法的执行过程中,并不能保证流守恒,但是却保持了一个“前置流”,前置流满足反对称性、容量限制、和放宽条件的流守恒特性,而这个放宽条件的流守恒特性就是指e(u)>=0,当e(u)>0时,则称顶点u溢出。下面对压入和重标记算法给出一个更直观的理解。
继续把流网络中的边看成是运输的管道,与之前Ford-Fulkerson思想有所不同的是,这里我们将每个顶点看成是一个水库,此时,上面所讲的余流实际上就是某一个水库中的蓄水量。为了算出最终的最大流,我们是不允许水库中有余流的,所以就要将存在余流的水库中的水向其他能聚集液体的任意大水库排放,这个操作就是压入操作。而压入的方式则取决于顶点的高度,顶点的高度是一个比较抽象的概念,我们可以认为当余流从水库u压入其他水库及以后的过程中,水库u的高度随之增加,我们仅仅把流往下压,即从较高顶点压入向较低顶点压,这样就不会出现刚把一个流从u压入v后马上又被往回压的死循环的情况了。源点的高度固定为|V|,汇点的高度固定为0,所有其他顶点的高度开始时都是0,并逐步增加。算法首先从源点输送尽可能多的流出去,也就是恰好填满从源点出发每条管道,当流进入一个中间顶点时,它就聚集在该顶点的水库中,并且最终将从这里继续向下压入。
注意
在压入的过程中可能会发生下列情况,离开顶点u且未被填满的管道所连接的顶点的高度与u相等或者高于u,根据我们的压入规则,这时候是没有办法继续往下压入的。为了使溢出顶点u摆脱其余流,就必须增加它的高度,即称为重标记操作。我们把u的高度增加到比其最低的相邻顶点(未被填满的管道所连接的顶点)的高度高一个单位。显然,当一个顶点被重标记后,至少存在着一条管道可以排除更多的流。
最终,有可能到达汇点的所有流均到达汇点,为了使前置流称为合法的流,根据算法的执行过程,算法会继续讲顶点的高度标记高于源点的固定高度|V|,以便把所有顶点中的余流送回源点。当除去源点和汇点的所有水库的水均为空时,我们就得到了最大流了。
(真正能想读懂的朋友最好还是要参阅《算法导论》)
附代码
(应该没问题,用过好几次了):
#include
using namespace std;
const int Max=1001;
int edges[Max][Max],f[Max][Max],c[Max][Max],e[Max],h[Max],vertices;
bool visited[Max]={false};i
nt push_relable(int s,int t)
{
//Initializeint queue[10*Max],head=0,tail=0;//队列可以用循环队列
queue[0]=s;
visited[s]=true;
for(int i=1;i<=edges[s][0];i++)
{
int u=edges[s][i];
f[s][u]=c[s][u];
f[u][s]=-c[s][u];
e[s]-=c[s][u];
e[u]=c[s][u];
visited[u]=true;
queue[++tail]=u;
}h[s]=vertices;
for(;head<=tail;head++){
int u=queue[head];
visited[u]=false;
//push
for(int i=1;i<=edges[u][0];i++)
{
int v=edges[u][i];
int resi=c[u][v]-f[u][v];
if(resi>0&&h[u]==h[v]+1)
{
int Min=min(resi,e[u]);
f[u][v]+=Min;
f[v][u]=-f[u][v];
e[u]-=Min;
e[v]+=Min;
if(v!=s&&v!=t&&!visited[v])
{
visited[v]=true;
queue[++tail]=v;
}
}
} //relable
if(e[u]>0&&u!=s&&u!=t)
{
visited[u]=true;
queue[++tail]=u;
int Min=(1<<30);
for(int i=1;i<=edges[u][0];i++)
{
int v=edges[u][i];int resi=c[u][v]-f[u][v];
if(resi>0&&h[v]
}
h[u]=max(h[u],Min+1);
}
}
//compute netflow
int flow=0;
for(int i=1;i<=edges[s][0];i++) flow+=f[s][edges[s][i]];
return flow;}
int main(){
cin>>vertices;int P;cin>>P;for(int i=1;i<=P;i++){
int a,b; cin>>a>>b;//必须记录反向边:正向边和反向边都有可能在残留。
edges[a][++edges[a][0]]=b; edges[b][++edges[b][0]]=a; c[a][b]=1; c[b][a]=0;
}cout<
(百度什么破编辑器,再不能写代码,百科别想办了,另外数学公式赶快弄好!)
参考资料
最新修订时间:2024-03-18 10:46
目录
概述
简介
参考资料