网络流详细总结
EK算法求最大流
$O(nm^2)$
inline void add(int a, int b, int c) { edge.pb(Edge(a, b, c, 0)); edge.pb(Edge(b, a, 0, 0)); int siz = edge.size(); G[a].pb(siz - 2); G[b].pb(siz - 1); } inline int Maxflow(int sta, int ed) { int res = 0; while (true) { queue<int> que; que.push(sta); memset(f, 0, sizeof(f)); f[sta] = INF; while (que.size()) { int u = que.front(); que.pop(); for (auto i : G[u]) { Edge& e = edge[i]; if (!f[e.b] && e.cap > e.flow) { pre[e.b] = i; f[e.b] = min(f[u], e.cap - e.flow); que.push(e.b); } } if (f[ed]) break; } if (!f[ed]) break; res += f[ed]; for (int i = ed; i != sta; i = edge[pre[i]].a) { edge[pre[i]].flow += f[ed]; edge[pre[i] ^ 1].flow -= f[ed]; } } return res; }
Dinic算法求最大流
$O(m\sqrt{n})$
inline void add(int a, int b, int c) { edge.pb(Edge(a, b, c, 0)); edge.pb(Edge(b, a, 0, 0)); int siz = edge.size(); G[a].pb(siz - 2); G[b].pb(siz - 1); } inline bool bfs(int sta, int ed) { queue<int> que; que.push(sta); memset(dep, 0, sizeof(dep)); memset(st, 0, sizeof(st)); dep[sta] = 0; st[sta] = true; while (que.size()) { int u = que.front(); que.pop(); for (auto i : G[u]) { Edge e = edge[i]; if (!st[e.b] && e.cap > e.flow) { dep[e.b] = dep[u] + 1; st[e.b] = true; que.push(e.b); } } } return st[ed]; } int dfs(int u, int a, int ed) { if (!a || u == ed) return a; int flow = 0, f = 0; for (int& i = cur[u]; i < G[u].size(); i++) { Edge& e = edge[G[u][i]]; if (dep[e.b] == dep[u] + 1 && (f = dfs(e.b, min(a, e.cap - e.flow), ed))) { flow += f; e.flow += f; edge[G[u][i] ^ 1].flow -= f; a -= f; if (!a) break; } } return flow; } inline int Maxflow(int sta, int ed) { int flow = 0; while (bfs(sta, ed)) { memset(cur, 0, sizeof(cur)); flow += dfs(sta, INF, ed); } return flow; }
二分图匹配
左边的点为$1~n$,右边的点为$n+1~n+m$
若$i$点能与$j$点匹配,则连一条$i->j$的边,流量为$1$,从$S$连$S->i$的边,流量为$i$的权值,从$j$连$j->T$的边,流量为$j$的权值,则最大匹配为最大流
无源汇求上下界可行流
每条边有流量上界和下界,问是否有一种方案使得所有点满足流量平衡的前提下,所有边满足流量限制
设每条边下届为$low[i]$,上界为$upp[i]$
则先给每条边$low[i]$的流量,之后设每个点流入的流量与流出的流量只差为$dout[i]$
若$dout[i]>0$,说明$i$需要流量流入,则连一条$S->i$流量为$dout[i]$的边
若$dout[i]<0$,说明$i$需要流量流出,则连一条$T->i$流量为$-dout[i]$的边
inline bool Dinic(int sta, int ed) { int flow = 0; while (bfs(sta, ed)) { memset(cur, 0, sizeof(cur)); flow += dfs(sta, INF, ed); } return flow == num; } int mian() { scanf("%d%d", &n, &m); int sta = n + m + 1, ed = n + m + 2; for (int i = 1; i <= m; i++) { scanf("%d%d%d%d", &a, &b, &low, &upp); low[i - 1] = low; add(a, b, upp - low); dout[a] -= low, dout[b] += low; } for (int i = 1; i <= n; i++) { if (dout[i] > 0) num += dout[i], add(sta, i, dout[i]); else add(i, ed, -dout[i]); } if (!Dinic(sta, ed)) puts("NO"), exit(0); puts("YES"); for (int i = 0; i < m; i++) printf("%d\n", edge[i << 1 | 1].cap - edge[i << 1 | 1].flow + low[i]); }
有源汇求上下界可行流
给定有源汇流量网络$G$,起点为$S$,终点为$T$,询问是否存在一种标定每条边流量的方式,使得每条边流量满足上下界同时除了源点和汇点每一个点流量平衡。
连一条$T->S$下届为$0$上界为$\infty $的边之后当无源汇上下界可行流求
有源汇求上下界最大流
求最大的可行流
求出可行流后加上残量网络从$S$到$T$的最大流即为答案
有源汇求上下界最小流
最小可行流
其他的和上面一样,只是最后的答案为可行流减去残量网络$T$到$S$的最大流
多源汇最大流
从$S$向每个源点连一条容量为$\infty $的边,从每个汇点向$T$连一条容量为$\infty $的边,答案即为从$S$到$T$的最大流
关键边问题
若增加某条边的容量之后整张图的最大流会增加,则该边是关键边
跑一遍最大流后,边$c(u,v)$为关键边的条件是:
$1、c(u,v)$满流
$2、$在残量网络中,$S->u$和$v->T$都有路径(路径上的边容量大于$0$)
最小割
最小割$=$最大流
求方案数:在残量网络上从$s$开始$DFS$,只走残量大于$0$的边,经过的点属于$S$
求割边数量:将每条边容量变为1,再跑一次最大流
最大权闭合子图
闭合图:图中所有节点的出边的终点都在图内
最大权闭合子图:求一个图中节点权值和最大的子图,且该子图是闭合图
求最大权闭合子图:从$S$向所有权值为正的点连容量为点权的边,从所有权值为负的点向$T$连容量为点权的绝对值的边,答案就为所有正点权和减最小割
最大密度子图
求一个图的子图$G=(E,V)$使得$\frac{|E|}{|V|}$最大
二分$g$,设$g=(\frac{|E|}{|V|})_{max}$
则$(\frac{|E|}{|V|})_{max}-g=0$
$|E|-g*|V|=0$
将其转换成最大权闭合子图,边的点权为$1$,点的点权为$-g$,求最大权闭合子图,答案$ans$便是$(|E|-g*|V|)_{max}$
若$ans-g>0$,说明$g$还可以再大,否则就要减小,以此类推二分
求方案数:在残量网络中从$S$只走没满流的边,经过的与$T$相连的点便是选中的点
最小点权覆盖集
选出一些点使得每条边的两个端点至少有一个点被选到,且点权和最小
给一条边的两端染黑白两色,使得每条边两个端点颜色都不同
从$S$向每个黑点连容量为点权的边,从每个白点向$T$连容量为点权的边,答案即为最小割
求方案:在残量网络上从$S$出发只走没满流的边,与$S$相连的没被经过的点和与$T$相连的被经过的点就为选中的点
最大点权独立集
每条边最多被选择一个端点,且点权和最大
最大点权独立集$-$点权和$-$最小点权覆盖集
费用流
连边:若有一条$a->b$的边且流量为$c$费用为$d$,则连两条边$add(a,b,c,d),add(b,a,0,-d)$
求最大流时,把找增广路的表示层数的$dep$改为表示最短路径的$dis$,找增广路的$bfs$用$spfa$来写最短路
用$Dicin$实现 $O(n·m·flow_{max})$:
inline void add(int a, int b, int c, int d) { num[dqx] = b; f[dqx] = c; cost[dqx] = d; nex[dqx] = h[a]; h[a] = dqx++; num[dqx] = a; f[dqx] = 0; cost[dqx] = -d; nex[dqx] = h[b]; h[b] = dqx++; } inline bool spfa(int S, int T) { queue<int> que; que.push(S); memset(dis, 0x7f, sizeof(dis)); memset(st, 0, sizeof(st)); dis[S] = 0, st[S] = true; while (que.size()) { int u = que.front(); que.pop(); st[u] = false; cur[u] = h[u]; for (int i = h[u]; ~i; i = nex[i]) { int j = num[i]; if (dis[j] > dis[u] + cost[i] && f[i]) { if (!st[j]) que.push(j), st[j] = true; dis[j] = dis[u] + cost[i]; } } } return dis[T] != INF; } int dfs(int u, int a, int ed, int& res) { if (u == ed || !a) return a; st[u] = true;//注意标记 int flow = 0, F; for (int& i = cur[u]; ~i; i = nex[i]) { int j = num[i]; if (st[j]) continue; if (dis[j] == dis[u] + cost[i] && (F = dfs(j, min(a, f[i]), ed, res))) { res += F * cost[i]; flow += F; a -= F; f[i] -= F; f[i ^ 1] += F; if (!a) break; } } st[u] = false; return flow; } inline int Maxflow(int S, int T, int& res) //res为费用 { int flow = 0, F; while (spfa(S, T)) while (F = dfs(S, INF, T, res)) flow += F; return flow; }
另一种实现方法,比上面一个快
inline void add(int a, int b, int flow, int cos) { num[dqx] = b; cost[dqx] = cos; Flow[dqx] = flow; nex[dqx] = h[a]; h[a] = dqx++; num[dqx] = a; cost[dqx] = -cos; Flow[dqx] = 0; nex[dqx] = h[b]; h[b] = dqx++; } inline bool spfa(int sta, int ed) { que.push(sta); memset(dis, 0x3f, sizeof(dis)); dis[sta] = 0; st[sta] = true; RE int u, i, j; while (!que.empty()) { u = que.front(); que.pop(); st[u] = false; for (i = h[u]; ~i; i = nex[i]) { j = num[i]; if (dis[j] > dis[u] + cost[i] && Flow[i]) { dis[j] = dis[u] + cost[i]; sur[j] = i; if (!st[j]) que.push(j); } } } return dis[ed] != INF; } inline int Maxflow(int sta, int ed, int &res) { int flow = 0; for (int f = INF, w; spfa(sta, ed); f = INF) { for (w = ed; w != sta; w = num[sur[w] ^ 1]) f = min(f, Flow[sur[w]]); for (w = ed; w != sta; w = num[sur[w] ^ 1]) Flow[sur[w]] -= f, Flow[sur[w] ^ 1] += f; res += dis[ed] * f; flow += f; } return flow; }