Dinic 的做法就是每次增广都重新计算d数组。然而,ISAP 改进的地方之一就是,其实没有必要马上更新d数组。这是因为,去掉一条边只可能令路径变得更长,而如果增广之前的残量网络存在另一条最短路,并且在增广后的残量网络中仍存在,那么这条路径毫无疑问是最短的。所以,ISAP 的做法是继续增广,直到遇到死路,才执行 retreat 操作。
int s,t;//源点、汇点
int p[N]; // 可增广路上的上一条弧的编号
int gap[N]; // 和 t 的最短距离等于 i 的节点数量
int cur[N]; // 当前弧下标
int d[N]; // 残量网络中节点 i 到汇点 t 的最短距离
queue<int> Q;
bool bfs(){
memset(d, -1, sizeof(int)*(n+1));
memset(gap, 0, sizeof(int)*(n+1));
d[t] = 0;
gap[0] = 1;
Q.push(t);
while(!Q.empty()){
int u = Q.front();
Q.pop();
for(int i = head[u]; i != -1; i = e[i].next){
int v = e[i].to;
if(d[v] != -1) continue;
d[v] = d[u] + 1;
gap[d[v]]++;
Q.push(v);
}
}
}
int augment(){
int a = INF;
// 从汇点到源点通过 p 追踪增广路径, a 为一路上最小的残量
for(int i = pre[t]; i != -1; i = pre[e[i^1].to]){
a = min(a, e[i].c - e[i].f);
}
// 从汇点到源点更新流量
for(int i = pre[t]; i != -1; i = pre[e[i^1].to]){
e[i].f += a;
e[i^1].f -= a;
}
return a;
}
ll Maxflow(){
ll flow = 0;
bfs();
int u = s;
p[u] = -1;
memcpy(cur, head, sizeof(head));
while(d[s] < n){
if(u == t){
flow += augment();
u = s;
}
bool flag = false;
for(int i = cur[u]; i != -1; i = e[i].next){
int v = e[i].to;
if(e[i].c > e[i].f && d[u] == d[v] + 1){
flag = true;
cur[u] = p[e[i].to] = i;
u = v;
break;
}
}
if(flag) continue;
// retreat
int Min = n;
for(int i = head[u]; i != -1; i = e[i].next){
if(e[i].c > e[i].f && d[e[i].to] < Min){
Min = d[e[i].to];
cur[u] = i;
}
}
if(--gap[d[u]] == 0) break;
d[u] = Min + 1;
gap[d[u]]++;
if(u != s) u = e[p[u]^1].to;
}
return flow;
}