最大流算法-最高标号预流推进(HLPP)
2017-05-15 19:59
267 查看
昨天我们学习了ISAP算法,它属于增广路算法的大类。今天学习的算法是预流推进算法中很高效的一类——最高标号预流推进(HLPP)。
每个节点是一个储水池,最开始源点有无限多的水。用一个队列维护需要处理的点。最开始把源点加进去,对于每一个当前点,我们把将这个点水池中有的流量沿着边(水管)推到相邻的点,然后把相邻的点加入队列中。
算法思想如此,但其中有一个问题:这样做有可能出现两个点一个推过来一个推回去,结果就死循环了。这时候我们给每个点引入一个高度来解决这个问题。
源点的高度为\(n\),汇点的高度为\(0\),其他点初始高度为0,我们规定,水往下一层流,即我们只推\(h[x]=h[v]+1\)的边\((x,v)\)。
如果一个点还有水,但是却无法推出去,即周围的点都比他高,那么我们就抬高这个点,因为\(h\)值是连续的,所以每次出现这种情况我们就给它加一。如果这个点根本就流不出去,那么最后它会被抬高到\(n+1\)的高度,回流给源点。
实测中ISAP跑得快,我估计是因为ISAP的复杂度上界非常松,而HLPP的上界是很紧的,导致ISAP随机下跑得超级快。
预流推进
预流推进是一种很直观的网络流算法。如果给到一个网络流让你手算,一般的想法是从源点开始流,遇到不够的就减掉,一直往前推到汇点。这就是预流推进算法的基本思想。每个节点是一个储水池,最开始源点有无限多的水。用一个队列维护需要处理的点。最开始把源点加进去,对于每一个当前点,我们把将这个点水池中有的流量沿着边(水管)推到相邻的点,然后把相邻的点加入队列中。
算法思想如此,但其中有一个问题:这样做有可能出现两个点一个推过来一个推回去,结果就死循环了。这时候我们给每个点引入一个高度来解决这个问题。
源点的高度为\(n\),汇点的高度为\(0\),其他点初始高度为0,我们规定,水往下一层流,即我们只推\(h[x]=h[v]+1\)的边\((x,v)\)。
如果一个点还有水,但是却无法推出去,即周围的点都比他高,那么我们就抬高这个点,因为\(h\)值是连续的,所以每次出现这种情况我们就给它加一。如果这个点根本就流不出去,那么最后它会被抬高到\(n+1\)的高度,回流给源点。
最高标号
Tarjan和Goldberg在1986年提出了最高标号预留推进算法,即把普通队列换成优先队列,每次取出高度最高的那个来推进。Cheriyan和Maheshwari在1988年证明了这样做的复杂度为\(O(n^2\sqrt m)\)。优化
喜闻乐见的gap优化,但和ISAP的形式不太一样。如果我们发现在给一个点抬高1的高度的时候,这个点原来的高度已经没有点了,那么我们直接把大于这个高度的点全部设为高度\(n+1\),让他们回流到源点去,因为根据算法,他们无法再有机会把水推到汇点(为什么不能有下面一个点抬上来形成路径呢?因为一个点的高度是所有相邻点高度最小值加一,所以不可能出现这种情况)。代码
依然是poj1273模版题,然而poj今天好像挂了。hdu1532是同一题。实测中ISAP跑得快,我估计是因为ISAP的复杂度上界非常松,而HLPP的上界是很紧的,导致ISAP随机下跑得超级快。
#include<cstdio> #include<cstring> #include<cctype> #include<vector> #include<queue> #include<algorithm> using namespace std; int read() { int x=0,f=1; char c=getchar(); for (;!isdigit(c);c=getchar()) if (c=='-') f=-1; for (;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } const int maxn=1e3+10; const int maxm=1e6+10; const int inf=2147483646; vector<int> inv[maxn]; struct edge { int v,w,nxt; } e[maxm<<1]; int h[maxn],tot,d[maxn],n,m,prs[maxn],gap[maxn]; bool able[maxn]; void add(int u,int v,int w) { e[++tot]=(edge){v,w,h[u]}; h[u]=tot; e[++tot]=(edge){u,0,h[v]}; h[v]=tot; } struct cmp { int x,h; cmp (int x=0,int h=0):x(x),h(h) {} inline bool operator < (const cmp &a) const {return h<a.h;} }; priority_queue<cmp> pq; bool push(int x,int y,int p) { int w=min(prs[x],e[p].w); e[p].w-=w,e[p^1].w+=w,prs[x]-=w,prs[y]+=w; return w; } void Gap(int l,int s,int t) { for (int i=1;i<=n;++i) if (i!=s && i!=t && l<d[i] && d[i]<=n) d[i]=n+1; } int maxflow(int s,int t) { while (!pq.empty()) pq.pop(); memset(prs,0,sizeof prs),memset(d,0,sizeof d),memset(gap,0,sizeof gap); d[s]=n,prs[s]=inf,pq.push(cmp(s,d[s])); while (!pq.empty()) { int x=pq.top().x; pq.pop(); if (!prs[x]) continue; for (int i=h[x],v=e[i].v;i;i=e[i].nxt,v=e[i].v) if ((x==s || d[x]==d[v]+1) && push(x,v,i) && v!=t && v!=s) pq.push(cmp(v,d[v])); if (x!=s && x!=t && prs[x]) { if (!(--gap[d[x]])) Gap(d[x],s,t); ++gap[++d[x]]; pq.push(cmp(x,d[x])); } } return prs[t]; } int main() { #ifndef ONLINE_JUDGE freopen("test.in","r",stdin); freopen("my.out","w",stdout); #endif while (~scanf("%d%d",&m,&n)) { memset(h,0,sizeof h),tot=1; for (int i=1;i<=m;++i) { int x=read(),y=read(),w=read(); if (!w) continue; add(x,y,w); } int ans=maxflow(1,n); printf("%d\n",ans); } return 0; }
相关文章推荐
- 网络最大流算法—最高标号预流推进HLPP
- poj-1273-Drainage Ditches-一般预流推进算法-最高标号预流推进算法-sap+gap优化
- poj1459最高标号预流推进算法解题报告
- USACO 4.2.1 网络流 最高标号预流推进(HLPP)
- 最高标号预留与推进算法 --- 就是要比 Dinic 快!
- sgu212:Data Transmission(HLPP最高标号预留推进)
- POJ1459 最高标号预留推进算法
- 最大流问题预流推进算法(最基本)
- 最大流问题预流推进算法(BFS优化)
- 最大流问题预流推进算法(邻接表relabel_to_front)
- 最大流算法——预流推进
- SGU212 Data Transmission(高标号预流推进算法)
- 最大流算法,Dinic,ISAP
- 网络流-最大流问题 ISAP 算法解释(转自Renfei Song's Blog)
- 34:E ·Power Network(最大流基础算法篇)
- 最大流算法的邻接阵实现
- 基于位图的最高优先级算法,256级
- 最大流算法---Edmond-Karp
- 对于求字符串中出现频率最高字符的算法实现
- 最大流问题:增广路径算法的比较