题解 P3376 【【模板】网络最大流】
Creeper_LKF
2017-11-08 22:00:26
大概是没有人写HLPP(Highest-Label Preflow Push)吧。于是我来写一发。
据说HLPP的复杂度是O(n^2\*√m),然而常数比较大,又由于本蒟蒻是几个月前写的板子,所以常数大了。
主要思路是:
高度标号:如果一个点的流量溢出了,那么我们需要通过“推流”将它推到适当的地方,所以引导“推流”的方法就是动态的高度标号
重(chong)标号:显然我们不会预先知道标号,然后高度还需正常引导流量,所以我们的Gap函数就是个作用
然而我们的溢出流量并不能一直呆在某个点,所以就要push到周围高度低的点
总之,HLPP的代码量小,细节多,复杂度小,常数大
具体细节在代码里
```cpp
#include <cstdio>
#include <cctype>
#include <queue>
#define INF 0x3f3f3f3f
#define MAXN 10010
#define MAXM 300010
using namespace std;
inline char get_char(){
static char buf[1000001], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1000000, stdin), p1 == p2) ? EOF : *p1 ++;
}
inline int read(){
int num = 0;
char c;
while (isspace(c = get_char()));
while (num = num * 10 + c - 48, isdigit(c = get_char()));
return num;
}
inline int min(int a, int b){
int c = (a - b) >> 31;
return (a & c) | (b & ~ c);
}
int n, m, s, t, tot = 1;
int beginx[MAXN], endx[MAXM], nxt[MAXM], res[MAXM];
inline void add_edge(int u, int v, int w){
nxt[++tot] = beginx[u], beginx[u] = tot, endx[tot] = v, res[tot] = w;
nxt[++tot] = beginx[v], beginx[v] = tot, endx[tot] = u, res[tot] = 0;
}
struct PQ{
int x,h;
PQ(int _x,int _h){
x = _x, h = _h;
}
bool operator < (const PQ &tar) const {
return h < tar.h;
}
};
int gap[MAXN], d[MAXN], ans[MAXN];//gap数组指向当前高度节点个数(相对的,不会实时更新),d数组指向每一个节点的高度,ans模拟每一个节点的流
inline bool push(int x, int y, int ptr) {
int w = min(ans[x], res[ptr]);//确定可推流量
res[ptr] -= w, res[ptr^1] += w;//更新反向边
ans[x] -= w, ans[y] += w;//推出溢出
return w;
}
inline void Gap(int val){
for (int i = 1; i <= n; ++i)
if(i != s && i != t && val < d[i] && d[i] <= n)
d[i] = n + 1;//检查出=除源点外高度比该点高的点
}
inline int HLPP(){
priority_queue<PQ> pq;
d[s] = n, ans[s] = INF, pq.push(PQ(s, d[s]));//从起点溢出了无限流量
int u;
while(!pq.empty()){
u = pq.top().x, pq.pop();
if(!ans[u]) continue;//不处理没有溢出的流
for(int i = beginx[u], v = endx[i]; i; i = nxt[i], v = endx[i])
if((u == s || d[u] == d[v] + 1) && push(u, v, i) && v != t && v != s)
pq.push(PQ(v, d[v]));//如果在源点或者是可以高度推流且允许改变流量且v不是源点或汇点
if (u != s && u != t && ans[u]){//如果当前点不是源点或汇点且已经有流溢出但没有在上一个for循环中推出去
if(!(--gap[d[u]])) Gap(d[u]);//如果当前高度只有一个节点进行Gap操作且等待更新(在提高流量后的下一次出队检查是否可以推出去)
++gap[++d[u]];//提升高度,否则就卡流量了
pq.push(PQ(u, d[u]));//等待处理(看新一轮处理能否把流量推出去)
}
}
return ans[t];
}
int main(){
n = read(), m = read(), s = read(), t = read();
for(int i = 1; i <= m; i++){
int u = read(), v = read(), r = read();
add_edge(u, v, r);
}
printf("%d", HLPP());
return 0;
}
```