网络流
算法进阶课(试听课)—— 网络流的基本概念_哔哩哔哩_bilibili
概念:
源点:只有流出去的点
汇点:只有流进来的点
流量:一条边上流过的流量
容量:一条边上可供流过的最大流量
残量:一条边上的容量-流量
流:指满足网络流的一种方案。
1,在一个有向图中,有n个点和m条边。有一个源点,和一个汇点,每条边有一个容量限制。
2,每条边的流量<=容量。
3,源点流出量=汇点流入量,其余各点流入量和流出量相等
当不存在增广路时,当前流为最大流。
P3376 【模板】网络最大流 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
EK
算法:
采用连事前向星的方式存储。
struct EDGE
{
int v, power, nxt;
};
EDGE edge[10004];
int tot = 0, head[202];
int n, m, s, t, book[202], pre[202];
int d[202], M = 2147483647;
int bfs();
void add(int u, int v, int w);
void EK();
int main()
{
cin >> n >> m >> s >> t;
int x, y, z;
for (int i = 1; i <= n; i++)
head[i] = -1;
for (int i = 1; i <= m; i++)
{
cin >> x >> y >> z;
add(x, y, z);
add(x,y,0);
}
EK();
return 0;
}
void add(int u, int v, int w)
{
edge[tot].v = v, edge[tot].power = w, edge[tot].nxt = head[u], head[u] = tot, tot++;
}
void EK()
{
long long ans = 0;
while (bfs())
{
ans = ans + d[t];
for (int i = t; i != s; i = edge[pre[i] ^ 1].v)
{
edge[pre[i]].power -= d[t];
edge[pre[i] ^ 1].power += d[t];
}
}
cout << ans;
}
int bfs()//bfs寻找增广路
{
queue<int> q;
memset(book, 0, sizeof(book));
memset(pre, 0, sizeof(pre));
memset(d, 0, sizeof(d));
q.push(s); book[s] = 1;
d[s] = M;
while (!q.empty())
{
int now = q.front(); q.pop();
for (int i = head[now]; i != -1; i = edge[i].nxt)
{
int v = edge[i].v, w = edge[i].power;
if (book[v] == 0 && w > 0)
{
q.push(v);
book[v] = 1;
d[v] = min(d[now], w);
pre[v] = i;
if (v == t)
return 1;
}
}
}
return 0;
}
dinic
算法:
对EK算法进行一些优化。
EK算法每次bfs只能找到一条增广路,
dinic算法利用分层图的原理,使用bfs将n个点分层,在使用dfs一次可以找到多条增广路。
#include<iostream>
#include<cmath>
#include<string>
#include<deque>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
struct EDGE
{
int v, power, nxt;
};
EDGE edge[10004];
int tot = 0, head[202], n, m, s, t, dep[202], M = 2147483647;
long long ans;
void add(int x, int y, int z);
void dinic();
int bfs();
int dfs(int x, int mi);
int main()
{
cin >> n >> m >> s >> t;
int x, y, z;
memset(head, -1, sizeof(head));
for (int i = 1; i <= m; i++)
{
cin >> x >> y >> z;
add(x, y, z);
add(y,x,0);
}
dinic();
return 0;
}
void add(int x, int y, int z)
{
edge[tot].v = y; edge[tot].power = z; edge[tot].nxt = head[x]; head[x] = tot; tot++;
}
void dinic()
{
long long ans = 0;
while (bfs())
{
ans = ans + dfs(s, M);
}
cout << ans;
}
int bfs()
{
memset(dep, 0, sizeof(dep));
dep[s] = 1;
queue<int> q;
q.push(s);
while (!q.empty())
{
int now = q.front(); q.pop();
for (int i = head[now]; i != -1; i = edge[i].nxt)
{
if (edge[i].power > 0 && dep[edge[i].v] == 0)
{
dep[edge[i].v] = dep[now] + 1;
q.push(edge[i].v);
}
}
}
if (dep[t] == 0)
return 0;
return 1;
}
int dfs(int x, int mi)//x表示当前节点,mi表示节点s到节点x的所有路径的最小容量
{
if (x == t)
return mi;
int out = 0;
for (int i = head[x]; i != -1; i = edge[i].nxt)
{
if (dep[edge[i].v] == dep[x] + 1 && edge[i].power > 0)
{
int d = dfs(edge[i].v, min(mi, edge[i].power));
mi -= d;
out += d;
edge[i].power -= d;
edge[i ^ 1].power += d;
}
}
if (out == 0)
dep[x] = 0;
return out;
}
最小费用最大流
P3381 【模板】最小费用最大流 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
和网络流类似。每条边再增加一个量:费用。
求网络流最大时的最小费用。
使用EK算法+SPFA优化可以解决。
EK算法求网络流时,求的是一条增广路,而不是花费最小的路。
使用SPFA代替bfs可以得到花费最小的增广路。
#include<iostream>
#include<cmath>
#include<string>
#include<deque>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int Maxm = 5e4 + 4;
const int Maxn = 5e3 + 3;
struct EDGE
{
int v, power, nxt, cost;
};
EDGE edge[Maxm * 2];
int tot, n, m, s, t, head[Maxn], Max = 2147483647, dis[Maxn], book[Maxn], flow[Maxn];
int pre[Maxn];
void add(int x, int y, int w, int c);
void MCMF();
int SPFA();
int main()
{
cin >> n >> m >> s >> t;
int x, y, w, c;
memset(head, -1, sizeof(head));
for (int i = 1; i <= m; i++)
{
cin >> x >> y >> w >> c;
add(x, y, w, c);
}
//n表示有n个点,从1-n
MCMF();
return 0;
}
void add(int x, int y, int w, int c)
{
edge[tot].v = y; edge[tot].power = w; edge[tot].cost = c; edge[tot].nxt = head[x]; head[x] = tot; tot++;
edge[tot].v = x; edge[tot].power = 0; edge[tot].cost = -c; edge[tot].nxt = head[y]; head[y] = tot; tot++;
}
void MCMF()
{
int ansflow = 0, anscost = 0;
while (SPFA())
{
ansflow += flow[t];
anscost += flow[t] * dis[t];
for (int i = t; i != s; i = edge[pre[i] ^ 1].v)
{
edge[pre[i]].power -= flow[t];
edge[pre[i] ^ 1].power += flow[t];
}
}
cout << ansflow << " " << anscost;
}
int SPFA()
{
queue<int> q;
memset(book, 0, sizeof(book));
memset(flow, 0, sizeof(flow));
for (int i = 1; i <= n; i++)
dis[i] = Max;
q.push(s);
book[s] = 1;
flow[s] = Max;
dis[s] = 0;
//dis[i]表示s到i的最短路径,(费用之和最小)
while (!q.empty())
{
int now = q.front(); q.pop();
book[now] = 0;
for (int i = head[now]; i != -1; i = edge[i].nxt)
{
int v = edge[i].v;
if (edge[i].power > 0 && dis[v] > dis[now] + edge[i].cost)
{
dis[v] = dis[now] + edge[i].cost;
flow[v] = min(flow[now], edge[i].power);
pre[v] = i;
if (book[v] == 0)
book[v] = 1, q.push(v);
}
}
}
if (dis[t] == Max)
return 0;
return 1;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 25岁的心里话