网络流笔记
Dinic 算法求最大流#
算法思想
首先用bfs通过网络流(残量)将图分层, 然后dfs搜索分层图,一次dfs可以搜出多条可行流, 但是这些可行流所经过的边数目都是相等的。
时间复杂度为
但是实际上在问题中,及少可以卡的满。 而且很多网络流问题是二分图, 这时时间复杂度约为
代码实现
code
namespace flow{
int dep[N + 5], cur[N + 5], h[N + 5], S, T;
struct Edge{
int v, nxt, val;
}e[M + 5];
int tot;
void add(int x, int y, int w) {
e[++tot] = Edge{y, h[x], w};
h[x] = tot;
e[++tot] = Edge{x, h[y], 0};
h[y] = tot;
}
void init() {
flow::S = n + 1; flow::T = n + 2;
tot = 1;
memset(h, 0, sizeof(h));
}
queue<int > q;
int bfs() {
memset(dep, 0, sizeof(dep)); q.push(S); dep[S] = 1;
while(!q.empty()) {
int u = q.front(); q.pop(); cur[u] = h[u];
for(int i = h[u], v; i; i = e[i].nxt)
if (e[i].val && !dep[v = e[i].v]) {
dep[v] = dep[u] + 1;
q.push(v);
}
}
return dep[T];
}
int dfs(int u, int flow) {
if (u == T || !flow)
return flow;
int ret = 0;
for(int &i = cur[u]; i; i = e[i].nxt) {
int v = e[i].v, d;
if ((dep[v] == dep[u] + 1) && (d = dfs(v, min(flow - ret, e[i].val)))) {
e[i].val -= d; e[i ^ 1].val += d; ret += d;
if (ret == flow) return flow;
}
}
return ret;
}
int dinic() {
int ans = 0;
while(bfs())
ans += dfs(S, inf);
return ans;
}
};
MCMF 费用流(spfa)#
算法思想
因为要求在流量最大的情况下要求费用最小, 所以考虑按照费用从小到大增广每一条可行流, 显然一条可行流的费用是路径上所有边的权值和。 所以我们可以采用SPFA算法。在SPFA的过程中记下路径, 然后依次对路径所有边的残量进行操作。 其实和EK算法很像,一次增广一条路径。
代码实现
code
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <bits/stdc++.h>
const int N = 1e5;
using namespace std;
int n, m, s, t, u, v, w, c;
void read(int &x) {
x = 0; int w = 1; char c = getchar(); for(; c < '0' || c > '9'; c = getchar()) if(c == '-') w = -1;
for(; c <= '9' && c >= '0'; c = getchar()) x = x * 10 + c - '0'; x *= w;
}
struct Edge{
int v, nxt, val, dis;
}e[N + 5];
int h[N + 5], tot, inq[N + 5], dis[N + 5], flow[N + 5], pre[N + 5];
int mf, mc, bk[N + 5];
void add(int x, int y, int z, int w) {
e[++tot] = Edge{y, h[x], z, w};
h[x] = tot;
}
queue<int > q;
int spfa() {
memset(inq, 0, sizeof(inq)); memset(dis, 0x7f, sizeof(dis)); memset(flow, 0x7f, sizeof(flow));
dis[s] = 0; inq[s] = 1; pre[t] = -1; q.push(s);
while(!q.empty()) {
int u = q.front(); q.pop(); inq[u] = 0;
// cout << u << endl;
for(int i = h[u], v; i; i = e[i].nxt)
if (e[i].val && dis[u] + e[i].dis < dis[v = e[i].v]) {
dis[v] = dis[u] + e[i].dis;
flow[v] = min(flow[u], e[i].val);
pre[v] = u; bk[v] = i;
if (!inq[v]) {
q.push(v);
// cout << u << ' ' << v << endl;
inq[v] = 1;
}
}
}
// cout << pre[t] << endl;
return pre[t] != -1;
}
int main() {
// freopen("t.in", "r", stdin);
read(n); read(m); read(s); read(t); tot = 1;
for(int i = 1; i <= m; ++i) {
read(u); read(v); read(w); read(c);
add(u, v, w, c); add(v, u, 0, -c);
}
while(spfa()) {
// cout << flow[t] << endl;
mf += flow[t]; mc += flow[t] * dis[t];
int now = t;
while(now != s) {
e[bk[now]].val -= flow[t]; e[bk[now]^1].val += flow[t];
now = pre[now];
}
}
printf("%d %d\n", mf, mc);
return 0;
}
有上下界的最大流#
算法思想
一个自然的想法就用 上界-下界 作为新网络的容量, 但是这样实际(加上默认的下界流量)不符合流量平衡。 然而在网络流中源点和汇点是可以不符合流量平衡的。 考虑在网络外再建一个源点和一个汇点, 定义
然后我们可以跑一遍最大流记为
如果等于, 记加入的
那么答案等于
注意这里
代码实现
code
#include <stdio.h>
#include <queue>
#include <iostream>
#include <algorithm>
#include <bits/stdc++.h>
#define LL long long
const int N = 400, M = 2e5;
#define inf 100000000
using namespace std;
#define L(i, s, t) for(int i = s; i <= t; ++i)
#define R(i, t, s) for(int i = t; i >= s; --i)
int n, m, s, t, S, T, h[N + 5], d[N + 5];
int dep[N + 5], cur[N + 5];
struct Edge{
int v, nxt, val;
}e[M + 5];
int tot;
void add(int x, int y, int w) {
e[++tot] = Edge{y, h[x], w};
h[x] = tot;
e[++tot] = Edge{x, h[y], 0};
h[y] = tot;
}
queue<int > q;
int bfs() {
memset(dep, 0, sizeof(dep)); q.push(S); dep[S] = 1;
while(!q.empty()) {
int u = q.front(); q.pop(); cur[u] = h[u];
for(int i = h[u], v; i; i = e[i].nxt)
if (e[i].val && !dep[v = e[i].v]) {
dep[v] = dep[u] + 1;
q.push(v);
}
}
return dep[T];
}
int dfs(int u, int flow) {
if (u == T || !flow)
return flow;
int ret = 0;
for(int &i = cur[u]; i; i = e[i].nxt) {
int v = e[i].v, d;
if ((dep[v] == dep[u] + 1) && (d = dfs(v, min(flow - ret, e[i].val)))) {
e[i].val -= d; e[i ^ 1].val += d; ret += d;
if (ret == flow) return flow;
}
}
return ret;
}
int dinic() {
int ans = 0;
// cout << "what" << endl;
while(bfs())
ans += dfs(S, inf);
return ans;
}
int main() {
// freopen("t.in", "r", stdin);
tot = 1;
scanf("%d%d%d%d", &n, &m, &s, &t); S = n + 1; T = n + 2; int sta = 0;
L(i, 1, m) {
int u, v, low, upp;
scanf("%d%d%d%d", &u, &v, &low, &upp);
add(u, v, upp - low);
d[v] += low; d[u] -= low;
}
L(i, 1, T)
if (d[i] > 0) add(S, i, d[i]), sta += d[i];
else add(i, T, -d[i]);
add(t, s, inf);
int ans = dinic();
// cout << e[tot - 1].val << endl;
if (ans != sta)printf("Impossble\n");
else {
ans = e[tot].val;
e[tot].val = 0; e[tot - 1].val = 0;
S = s; T = t; ans += dinic();
printf("%d\n", ans);
}
return 0;
}
有上下界的最小流#
算法思想
基本等于上面的的 有上下界的最大流, 唯一不同的是 找出可行流后要想办法减流, 删去
代码实现
code
#include <stdio.h>
#include <queue>
#include <iostream>
#include <algorithm>
#include <bits/stdc++.h>
#define LL long long
const int N = 6e4, M = 5e5;
#define inf 2147483647
using namespace std;
#define L(i, s, t) for(int i = s; i <= t; ++i)
#define R(i, t, s) for(int i = t; i >= s; --i)
int n, m, s, t, S, T, h[N + 5], d[N + 5];
int dep[N + 5], cur[N + 5];
struct Edge{
int v, nxt, val;
}e[M + 5];
int tot;
void add(int x, int y, int w) {
e[++tot] = Edge{y, h[x], w};
h[x] = tot;
e[++tot] = Edge{x, h[y], 0};
h[y] = tot;
}
queue<int > q;
int bfs() {
memset(dep, 0, sizeof(dep)); q.push(S); dep[S] = 1;
while(!q.empty()) {
int u = q.front(); q.pop(); cur[u] = h[u];
for(int i = h[u], v; i; i = e[i].nxt)
if (e[i].val && !dep[v = e[i].v]) {
dep[v] = dep[u] + 1;
q.push(v);
}
}
return dep[T];
}
int dfs(int u, int flow) {
if (u == T || !flow)
return flow;
int ret = 0;
for(int &i = cur[u]; i; i = e[i].nxt) {
int v = e[i].v, d;
if ((dep[v] == dep[u] + 1) && (d = dfs(v, min(flow - ret, e[i].val)))) {
e[i].val -= d; e[i ^ 1].val += d; ret += d;
if (ret == flow) return flow;
}
}
return ret;
}
int dinic() {
int ans = 0;
// cout << "what" << endl;
while(bfs())
ans += dfs(S, inf);
return ans;
}
int main() {
// freopen("t.in", "r", stdin);
tot = 1;
scanf("%d%d%d%d", &n, &m, &s, &t); S = n + 1; T = n + 2; int sta = 0;
L(i, 1, m) {
int u, v, low, upp;
scanf("%d%d%d%d", &u, &v, &low, &upp);
add(u, v, upp - low);
d[v] += low; d[u] -= low;
}
L(i, 1, T)
if (d[i] > 0) add(S, i, d[i]), sta += d[i];
else add(i, T, -d[i]);
add(t, s, inf);
int ans = dinic();
// cout << e[tot - 1].val << endl;
if (ans != sta)printf("please go home to sleep\n");
else {
ans = e[tot].val;
e[tot].val = 0; e[tot - 1].val = 0;
S = t; T = s; ans -= dinic();
printf("%d\n", ans);
}
return 0;
}
待补
dijkstra费用流#
有上下界费用流#
带负圈的费用流#
作者:Aurora
出处:https://www.cnblogs.com/aurora2023/p/17092109.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】