言琢დ @ 2024-02-04 19:40:40
如题,我调整了为
signed main(){
n = init(), m = init(), S = init(), T = init();
for (int i = 1; i <= m; ++i)
add(init(), init(), init());
int ans = 0;
while (true) {
memset(dis, -1, sizeof(dis));
memset(go, 0, sizeof(go));
std::queue <int> Q;
Q.push(S); dis[S] = 0;
while (!Q.empty()) {
int u = Q.front(); Q.pop();
go[u] = head[u]; // u 的第一条有效边,必须是 head[u],否则顺序会出错
for (int i = head[u]; i; i = s[i].next) {
int v = s[i].to, w1 = s[i].flow, w2 = s[i].lim;
if (w1 >= w2) continue;
if (dis[v] != -1) continue;
dis[v] = dis[u] + 1; // 分层图距离计算
Q.push(v);
}
}
if (dis[T] == -1) break;
ans += dfs(S, MAX_INT);
}
print(ans), putchar('\n');
}
上面这篇代码,在每次结点
而我考虑到按照遍历顺序来说,满足
signed main(){
n = init(), m = init(), S = init(), T = init();
for (int i = 1; i <= m; ++i)
add(init(), init(), init());
int ans = 0;
while (true) {
memset(dis, -1, sizeof(dis));
memset(go, 0, sizeof(go));
std::queue <int> Q;
Q.push(S); dis[S] = 0;
while (!Q.empty()) {
int u = Q.front(); Q.pop();
for (int i = head[u]; i; i = s[i].next) {
int v = s[i].to, w1 = s[i].flow, w2 = s[i].lim;
if (w1 >= w2) continue;
if (dis[v] != -1) continue;
if (!go[u]) go[u] = i; // 按照遍历顺序,u 的第一条有效边
dis[v] = dis[u] + 1; // 分层图距离计算
Q.push(v);
}
}
if (dis[T] == -1) break;
ans += dfs(S, MAX_INT);
}
print(ans), putchar('\n');
}
按照我的期望来说,应该是两篇代码均为正确,并且第二篇较快。
实际情况是两篇代码均为正确,并且第一篇较快。
第一篇用时 65ms,第二篇用时 334ms。
感觉第二篇进行的运算次数应该严格小于第一篇啊(毕竟跳过了一些完全无效的边)
为啥会反而慢这么多?
by 言琢დ @ 2024-02-04 19:42:15
Dinic 算法中 dfs 过程如下,为了防止占用页面太长,完整代码就不贴了,应该不影响阅读吧。(用于对比的两篇代码其他部分都相同,只有
int dfs(int u, int now){
// now 表示从 s -> u 一路过来的合法流量最小值
if (u == T) return now;
int ans = 0; // ans 表示已经流出去的合法流量
for (int i = go[u]; i; i = s[i].next) {
if (ans >= now) break; // 从 u 开始已经流不出去流量
go[u] = i;
// 当前弧优化:每次总访问一条可能有效的边
// 由于结点 u 可能被不同的前驱结点访问,所以别的流量再流进来时,编号为 i 的边之前的边一定已经流光了
int v = s[i].to, w1 = s[i].flow, w2 = s[i].lim;
if (dis[v] != dis[u] + 1) continue;
// 分层图优化
if (w1 >= w2) continue;
// 还有流量剩余
int cnt = dfs(v, mn(now - ans, w2 - w1));
// 从 v 流出去的流量,必须满足 <= now-ans 且 <= w2-w1
// 其中 ans 表示已经流出去,不复存在的流量,因此需要从 now 中去除
if (!cnt) {
dis[v] = MAX_INT; continue;
}
// 若从 v 已经流不出去流量,说明从 v 出去已经达到饱和
s[i].flow += cnt;
s[i ^ 1].flow -= cnt; // 流量修改
ans += cnt; // 统计已经流出去的流量
}
return ans;
}