structgraph_t { structedge_t { // 链式前向星 int v, nxt, flw; // 实现上为了方便,直接修改流量 } edges[MAXM]; int head[MAXN], e_cnt = 1; // u^1 得到 u 的反向边
void _add(int u, int v, int f) { edges[++e_cnt] = {v, head[u], f}, head[u] = e_cnt; } voidadd(int u, int v, int f){ _add(u, v, f), _add(v, u, 0); }
int dis[MAXN], now[MAXN]; // 当前弧优化,从未流尽的边开始考虑 boolbfs() { memset(dis, 0x3f, sizeof(dis)); memcpy(now, head, sizeof(now)); queue<int> q; q.push(s), dis[s] = 0; while (q.size()) { int u = q.front(); q.pop(); for (int i = head[u]; i; i = edges[i].nxt) { if (edges[i].flw && dis[edges[i].v] > dis[u] + 1) { // 分层 dis[edges[i].v] = dis[u] + 1; q.push(edges[i].v); } } } return dis[t] < INF; }
intdfs(int u, int flow) { if (u == t || !flow) return flow; int maxflow = 0; for (int i = now[u]; i && flow; now[u] = i = edges[i].nxt) { int v = edges[i].v; if (edges[i].flw && dis[v] == dis[u] + 1) { // 一层一层来 int k = dfs(v, min(flow, edges[i].flw)); if (!k) dis[v] = INF; // 删去流尽的边 maxflow += k, flow -= k, edges[i].flw -= k, edges[i ^ 1].flw += k; // 应用修改 } } return maxflow; }
intdinic() { int maxflow = 0, x = 0; while (bfs()) { while ((x = dfs(s, INF))) maxflow += x; } return maxflow; } } G;
时间复杂度证明
最坏时间复杂度 O(n2m) ,大多数情况远远跑不满 。
考虑每次增广,在使用当前弧优化的情况下,对于每个点,显然当前弧 now 至多变化 m 次,共 n 个点,故单次时间复杂度 O(nm) 。
structgraph_t { structedge_t { int v, nxt, flw; } edges[MAXM]; int head[MAXN], e_cnt = 1;
void _add_edge(int u, int v, int f) { edges[++e_cnt] = {v, head[u], f}, head[u] = e_cnt; } voidadd_edge(int u, int v, int f){ _add_edge(u, v, f), _add_edge(v, u, 0); }
// 从汇点开始标记高度 boolbfs() { memset(h, 0, sizeof(h)); queue<int> q; q.push(t); h[t] = 0; while (q.size()) { int u = q.front(); q.pop(); for (int i = head[u]; i; i = edges[i].nxt) { int v = edges[i].v; if (edges[i ^ 1].flw && h[v] > h[u] + 1) { h[v] = h[u] + 1; q.push(v); } } } if (h[s] < 1e9) { for (int i = 1; i <= n; i++) { if (h[i] >= 1e9) h[i] = n + 100; } return h[s] = n, true; } elsereturnfalse; }
// 向高度正好小 1 的点流超额流 voidpush(int u) { for (int i = head[u]; i && val[u]; i = edges[i].nxt) { int v = edges[i].v; if (edges[i].flw && h[v] == h[u] - 1) { int k = min(edges[i].flw, val[u]); edges[i].flw -= k, edges[i ^ 1].flw += k; val[u] -= k, val[v] += k; if (v != s && v != t && !in_q[v]) q.push({h[v], v}), in_q[v] = true; } } }
// 重贴标签,让 h 刚好比相连的 h 最小的大 1 voidrelabel(int u) { h[u] = 1e9; for (int i = head[u]; i; i = edges[i].nxt) { int v = edges[i].v; if (edges[i].flw && h[u] > h[v] + 1) h[u] = h[v] + 1; } if (h[u] >= 1e9) h[u] = n + 100; }
inthlpp() { if (!bfs()) return0; // 高度为 h 的有多少个 for (int i = 1; i <= n; i++) gap[h[i]]++; // 从源点推出,忽略高度 for (int i = head[s]; i; i = edges[i].nxt) { int v = edges[i].v; if (edges[i].flw) { int k = edges[i].flw; edges[i].flw -= k, edges[i ^ 1].flw += k; val[v] += k; if (v != s && v != t && !in_q[v]) q.push({h[v], v}), in_q[v] = true; } }
while (q.size()) { int u = q.top().second; // 取出高度最高的点 q.pop(); in_q[u] = false; push(u); if (val[u]) { // 没流完 gap[h[u]]--; if (!gap[h[u]]) { // gap 优化 // 比它高的点不能把流量推送到 t 了 // 高度设为 n + 1 尽快推送回 s for (int i = 1; i <= n; i++) { if (i != s && i != t && h[i] > h[u] && h[i] < n + 1) h[i] = n + 1; } } relabel(u); // 重贴标签 gap[h[u]]++; q.push({h[u], u}); in_q[u] = true; } } return val[t]; } } G;
最小割
一个有向图,有边权,它的最小割就是切断一些边,把这个图切成两半,让源点 s 和汇点 t 不存在一条路径,同时割断的边边权和最小。
structgraph_t { structedge_t { int v, nxt, flw, cst; } edges[MAXM]; int head[MAXN], e_cnt = 1; void _add_edge(int u, int v, int f, int c) { edges[++e_cnt] = {v, head[u], f, c}, head[u] = e_cnt; } voidadd_edge(int u, int v, int f, int c){ _add_edge(u, v, f, c), _add_edge(v, u, 0, -c); }
int dis[MAXN], now[MAXN]; bool vis[MAXN]; boolspfa()// 最短路 { memset(dis, 0x3f, sizeof(dis)), memcpy(now, head, sizeof(now)); queue<int> q; q.push(s), dis[s] = 0; while (q.size()) { int u = q.front(); q.pop(); vis[u] = false; for (int i = head[u]; i; i = edges[i].nxt) { int v = edges[i].v; if (edges[i].flw && dis[v] > dis[u] + edges[i].cst) { dis[v] = dis[u] + edges[i].cst; if (!vis[v]) vis[v] = true, q.push(v); } } } return dis[t] < INF; }
pair<int, int> dfs(int u, int flow)// 和最大流差不多 { if (u == t || !flow) return {flow, 0}; int maxflow = 0, mincost = 0; vis[u] = true; for (int i = now[u]; i && flow; now[u] = i = edges[i].nxt) { int v = edges[i].v; if (edges[i].flw && dis[v] == dis[u] + edges[i].cst && !vis[v]) { auto p = dfs(v, min(flow, edges[i].flw)); int x = p.first, y = p.second; if (!x) dis[v] = INF; flow -= x, maxflow += x, edges[i].flw -= x, edges[i ^ 1].flw += x; mincost += y + x * edges[i].cst; // 更新贡献 } } vis[u] = false; return {maxflow, mincost}; }
pair<int, int> dinic() { int maxflow = 0, mincost = 0; while (spfa()) { while (true) { auto p = dfs(s, INF); maxflow += p.first, mincost += p.second; if (!p.first) break; } } return {maxflow, mincost}; } } G;