网络流
给定一个网络,有源点和汇点,现在要往源点灌水,问每单位时间可以从汇点出多少水,并且每一条边有限流。
P3376【模板】网络最大流
一个几乎没用的东西:FF
思路
我们很显然会有个思路,就是每次 \(DFS\) 搜索,找到一条路径,并且是可以增广的(限流还没达到),那么就增广它,可是如果遇到一条路径走会满流,但分开走就能流出最大流的情况,所以我们需要一个反悔的机会,可以将剩余可以流的水量建一条正向边形成残余网络,已经流的建一条反向边,于是就有了一个反悔的机会。
EK
思路
将 \(DFS\) 改成 \(BFS\) 即可,这样可以保证每次找到的路径是最短的,但是我们在增广的时候需要把路径弄出来,所以我们用一个 \(pre\) 数组来记录上一个结点,这样就可以记录路径了。
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;
}
时间复杂度:\(O(nm^2)\)
Dinic
每次对残余网络进行分层,将非可是分层的边(树上就是横向边)删了,然后找当前图的最大流,及找到增广路径,然后增广,将当前图的最大流并入最终的最大流。
优化
- 在 \(DFS\) 途中,每次将当前边,设为当前点第一条边(注意最后你要还原),因为当处理完这条边后,再处理的话,必然会再一条被增广的路径上被卡死。
- 在 \(DFS\) 过程中,如果处理完一条边的答案为 \(0\),那我们标记一下,返回过程中就不走这了,同样的要还原。
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;
}
时间复杂度
最差:\(O(n^2m)\) 若为二分图,可以到 \(O(m\sqrt n)\)
最小割
最小割等于最大流
费用流
直接在跑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;
}