【笔记】正确的当前弧优化
UOJ 上测了一下某“当前弧优化”的写法,虽然那题是准备给 ISAP 的模板,但是 \(O(n^2m)\) 肯定能过的点也没有跑过。这就令人好奇了。。
当前弧优化应当这么写(原因在注释里):
ll dfs(int u, ll flow) {
if (u == T) return flow;
ll rest = flow;
for (int &i = nhd[u]; i; i = e[i].nxt) {
int v = e[i].v;
if (e[i].f && d[v] == d[u] + 1) {
ll k = dfs(v, min(rest, (ll)e[i].f));
if (!k) d[v] = 0;
rest -= k, e[i].f -= k, e[i ^ 1].f += k;
}
// rest 为 0 不代表不能继续走这条边扩展增广路,因为 rest 实际上是上一个点跑过来的流量。后面可能从另一条路径到达这条边,并且能通过这条边扩展,所以不能把 nhd[u] 设置为这条边的 nxt 导致以后都跳过这条边。
if (!rest) break;
}
return flow - rest;
}
而不是蓝书上写的:
ll dfs(int u, ll flow) {
if (u == T) return flow;
ll rest = flow;
for (int &i = nhd[u]; i && rest; i = e[i].nxt) {
int v = e[i].v;
if (e[i].f && d[v] == d[u] + 1) {
ll k = dfs(v, min(rest, (ll)e[i].f));
if (!k) d[v] = 0;
rest -= k, e[i].f -= k, e[i ^ 1].f += k;
}
}
return flow - rest;
}
其实就是,仅当从某条边出发无法增广的时候才能把该边抛弃掉。如果因为 rest
不够而不能增广,不能说明该边无法增广。
顺便给一个单路增广的写法,从这个写法扩展完全能得到正确的多路增广:
int dfs(int u, int flow) {
if (u == T) return flow;
for (int &i = nhd[u]; i; i = e[i].nxt) {
int v = e[i].v;
if (e[i].f && d[v] == d[u] + 1) {
int k = dfs(v, min(flow, e[i].f));
if (!k) continue;
e[i].f -= k, e[i ^ 1].f += k;
return k;
}
}
return 0;
}
并且单路增广和多路增广在洛谷上用时几乎没有差别,在 UOJ 上单路比多路多 T 了两个点。