网络流
给定一个网络,有源点和汇点,现在要往源点灌水,问每单位时间可以从汇点出多少水,并且每一条边有限流。
P3376【模板】网络最大流
一个几乎没用的东西:FF
思路
我们很显然会有个思路,就是每次
EK
思路
将
code
#include <iostream> #include <queue> using namespace std; using ll = long long; const int MaxN = 210, MaxM = 2 * 5010; struct Edge { ll v, w, nxt; } e[MaxM]; ll h[MaxN], ans; int n, m, s, t, cnt = 1; bool vis[MaxN]; queue<int> q; pair<int, int> p[MaxN]; ll G(int x, ll minx = 1e18) { if (x == s) { return minx; } ll res = G(p[x].first, min(minx, e[p[x].second].w)); e[p[x].second ^ 1].w += res; e[p[x].second].w -= res; return res; } void Record(int u, int i) { if (vis[e[i].v] || !e[i].w) { return; } vis[e[i].v] = 1; p[e[i].v] = {u, i}; q.push(e[i].v); } ll BFS() { for (int i = 1; i <= n; i++) { vis[i] = 0, p[i] = {0, 0}; } queue<int>().swap(q); for (q.push(s), vis[s] = 1; !q.empty(); q.pop()) { int u = q.front(); if (u == t) { return G(t); } for (int i = h[u]; ~i; i = e[i].nxt) { Record(u, i); } } return -1; } int main() { ios::sync_with_stdio(0), cin.tie(0); cin >> n >> m >> s >> t; fill(h + 1, h + n + 1, -1); for (ll i = 1, u, v, w; i <= m; i++) { cin >> u >> v >> w; e[++cnt] = {v, w, h[u]}, h[u] = cnt; e[++cnt] = {u, 0, h[v]}, h[v] = cnt; } for (ll res; ~(res = BFS()); ans += res) { } cout << ans << '\n'; return 0; }
时间复杂度:
Dinic
每次对残余网络进行分层,将非可是分层的边(树上就是横向边)删了,然后找当前图的最大流,及找到增广路径,然后增广,将当前图的最大流并入最终的最大流。
优化
- 在
途中,每次将当前边,设为当前点第一条边(注意最后你要还原),因为当处理完这条边后,再处理的话,必然会再一条被增广的路径上被卡死。 - 在
过程中,如果处理完一条边的答案为 ,那我们标记一下,返回过程中就不走这了,同样的要还原。
code
#include <iostream> #include <queue> using namespace std; using ll = long long; const int MaxN = 210, MaxM = 2 * 5010; struct IF { struct Edge { ll v, w, nxt; } e[MaxM]; ll dis[MaxN], h[MaxN], th[MaxN], ans, n, s, t, cnt; bool vis[MaxN]; queue<int> q; IF() { n = s = t = ans = 0, cnt = 1; fill(h, h + MaxN, -1); fill(dis, dis + MaxN, 0); } void Record(int u, int i) { if (vis[e[i].v] || !e[i].w) { return; } vis[e[i].v] = 1; dis[e[i].v] = dis[u] + 1; q.push(e[i].v); } bool BFS() { for (int i = 1; i <= n; i++) { vis[i] = 0, th[i] = h[i]; } queue<int>().swap(q); for (q.push(s), vis[s] = 1, dis[s] = 1; !q.empty(); q.pop()) { int u = q.front(); if (u == t) { continue; } for (int i = h[u]; ~i; i = e[i].nxt) { Record(u, i); } } return vis[t]; } ll DFS(int x, ll f) { if (x == t || !f) { return f; } ll res = 0; for (int i = th[x]; ~i && f - res > 0; i = e[i].nxt) { th[x] = i; if (e[i].w && dis[x] + 1 == dis[e[i].v]) { ll tmp = DFS(e[i].v, min(f - res, e[i].w)); if (!tmp) { dis[e[i].v] = 1e18; } res += tmp; e[i].w -= tmp; e[i ^ 1].w += tmp; } } return res; } void insert(int u, int v, ll w) { e[++cnt] = {v, w, h[u]}, h[u] = cnt; e[++cnt] = {u, 0, h[v]}, h[v] = cnt; } ll Solov() { for (; BFS(); ans += DFS(s, 1e18)) { } return ans; } }; int n, m, s, t; int main() { ios::sync_with_stdio(0), cin.tie(0); cin >> n >> m >> s >> t; IF ans; ans.n = n, ans.s = s, ans.t = t; for (ll i = 1, u, v, w; i <= m; i++) { cin >> u >> v >> w; ans.insert(u, v, w); } cout << ans.Solov() << '\n'; return 0; }
时间复杂度
最差:
最小割
最小割等于最大流
费用流
直接在跑dinic的时候将BFS改成spfa然后就可以过了,有一些细节优化是不可少的
code
#include <iostream> #include <queue> using namespace std; using ll = long long; namespace Dinic { const ll inf = 1e15; const int MaxN = 5e3 + 10, MaxM = 5e4 + 10; struct Edge { ll to, w, c, nxt; } e[MaxM << 1]; ll cur[MaxN], dis[MaxN], head[MaxN], n, m, cnt, s, t, maxflow, mincost; bool vis[MaxN], ins[MaxN]; deque<int> q; void add(int u, int v, ll w, ll c) { e[++cnt] = {v, w, c, head[u]}, head[u] = cnt; e[++cnt] = {u, 0, -c, head[v]}, head[v] = cnt; } void Record(int u, int v, ll w, ll c) { if (!w || dis[v] <= dis[u] + c) return; dis[v] = dis[u] + c; (!vis[v]) && ((q.empty() || dis[v] < dis[q.front()]) ? (q.push_front(v)) : (q.push_back(v)), vis[v] = 1); } bool Spfa() { deque<int>().swap(q); for (int i = 1; i <= n; i++) cur[i] = head[i], dis[i] = inf, vis[i] = 0; for (Record(0, s, inf, 0); !q.empty();) { ll u = q.front(); q.pop_front(), vis[u] = 0; for (int i = head[u]; ~i; i = e[i].nxt) { Record(u, e[i].to, e[i].w, e[i].c); } } return dis[t] != inf; } ll DFS(ll x, ll f) { if (x == t) return f; ins[x] = 1; ll res = f; for (int i = cur[x]; ~i && res; i = e[i].nxt) { cur[x] = i; if (ins[e[i].to] || !e[i].w || dis[x] + e[i].c != dis[e[i].to]) continue; ll tmp = DFS(e[i].to, min(res, e[i].w)); (tmp) && (res -= tmp, e[i].w -= tmp, e[i ^ 1].w += tmp); } (res == f) && (dis[x] = inf); ins[x] = 0; return f - res; } void init() { cin >> n >> m >> s >> t; fill(head + 1, head + n + 1, -1), cnt = 1, maxflow = mincost = 0; for (int i = 1, u, v, w, c; i <= m; i++) { cin >> u >> v >> w >> c; add(u, v, w, c); } } void Solve() { ll tmp = 0; for (init(); Spfa(); tmp = DFS(s, inf), maxflow += tmp, mincost += dis[t] * tmp) { } } }; // namespace Dinic using namespace Dinic; int main() { ios::sync_with_stdio(0), cin.tie(0); Solve(); cout << maxflow << " " << mincost << endl; return 0; }
本文作者:yabnto
本文链接:https://www.cnblogs.com/ybtarr/p/18021748
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
2023-08-22 树距离