昨天我們學習了ISAP算法,它屬於增廣路算法的大類。今天學習的算法是預流推進算法中很高效的一類——最高標號預流推進(HLPP)。
預流推進
預流推進是一種很直觀的網絡流算法。如果給到一個網絡流讓你手算,一般的想法是從源點開始流,遇到不夠的就減掉,一直往前推到匯點。這就是預流推進算法的基本思想。
每個節點是一個儲水池,最開始源點有無限多的水。用一個隊列維護需要處理的點。最開始把源點加進去,對於每一個當前點,我們把將這個點水池中有的流量沿着邊(水管)推到相鄰的點,然后把相鄰的點加入隊列中。
算法思想如此,但其中有一個問題:這樣做有可能出現兩個點一個推過來一個推回去,結果就死循環了。這時候我們給每個點引入一個高度來解決這個問題。
源點的高度為\(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;
}
