最大流
EK算法
复杂度O(n*m*m)
EK算法思路:1、利用bfs寻找增广路(因此也叫增广路算法) 2、若存在增广路,维护最大流
#include<iostream> #include<queue> #include<algorithm> #include<cstdio> #include<cstring> using namespace std; const int inf = 0x7fffffff; const int maxn = (int)2e2 + 10; int n, m; //顶点数和边数 int G[maxn][maxn]; //存图G[i][j]=c 为点i到点j的边的容量为c int pre[maxn]; //存储增广路,记录前驱,pre[i]=j为i的前驱为j int flow[maxn]; //寻找增广路使记录流向每个节点的流量,flow[u]=k为起始点s流向u点的流量为k int bfs(int s,int t){ //寻找一条从s到t的增广路,如果找到返回增广路的可行流,如果没找到返回-1用于标记 queue<int> q; for (int i = 0; i <= n; i++) pre[i] = -1; pre[s] = 0; flow[s] = inf; q.push(s); while (!q.empty()){ int p = q.front(); q.pop(); if (p == t) break; //找到一条增广路 for (int u = 1; u <= n; u++){ //不是原点 if (u!=s && pre[u] == -1 && G[p][u] > 0){ //该点没被走过并且该点的流量大于0 pre[u] = p; flow[u] = min(flow[p],G[p][u]); q.push(u); } } } if (pre[t] == -1) return -1; return flow[t]; } int EK(int s,int t){ //计算最大流,EK算法,复杂度O(n*m^2) int maxsum = 0, delta; while ((delta=bfs(s,t))!=-1){ //delta = bfs(s, t); //if (delta == -1) break; int p = t; //从终点出发 while (p != s){ G[pre[p]][p] -= delta; //正向边 G[p][pre[p]] += delta; //反向边 p = pre[p]; //更新当前点 } maxsum += delta; } return maxsum; } int main() { int a, b, c; cin >> n >> m; memset(G, 0, sizeof(G)); memset(flow, 0, sizeof(flow)); for (int i = 0; i < m; i++){ scanf("%d%d%d",&a,&b,&c); G[a][b] += c; } printf("%d\n",EK(1,n)); return 0; }
dinic算法
复杂度O(n*n*m)
dinic的实现是基于EK算法的,对EK算法进行优化
优化 1、利用bfs对图进行分层 2、dfs实现多次增广
#include<iostream> #include<queue> #include<algorithm> #include<cstdio> using namespace std; const int maxn = 220; const int inf = 0x7fffffff; struct Edge{ int to, w, next; }; Edge e[maxn * 2]; int depth[maxn]; //顶点的层数 int head[maxn]; //实现链式前向星 int n, m, s, t; int cnt; //边的编号 void init(){ cnt = 0; s = 1, t = n; for (int i = 0; i <= n; i++) head[i] = -1; } void add_edge(int u, int v, int w){ //加边 e[cnt].to = v; e[cnt].w = w; e[cnt].next = head[u]; head[u] = cnt++; } int bfs(int s,int t){ //分层 queue<int> q; for (int i = 0; i <= n; i++) depth[i] = inf; q.push(s); depth[s] = 0; while (!q.empty()){ int p = q.front(); q.pop(); for (int i = head[p]; ~i; i = e[i].next){ //遍历以p为起始的边 if (e[i].w>0 && depth[p]+1<depth[e[i].to]){ //如果是可行边,并且下一个点的深度和当前点的深度相差大于1 depth[e[i].to] = depth[p] + 1; //更新下一个点的深度 q.push(e[i].to); } } } return depth[t] != inf; } int dfs(int u,int flow){ //多次增广 当前点u和flow为当前流量,一开始假设为inf if (u == t) //如果找到汇点 return flow; for (int i = head[u]; ~i; i = e[i].next){ if (e[i].w > 0 && depth[u] + 1 == depth[e[i].to]){ int delta = dfs(e[i].to, min(flow,e[i].w)); //向下增广 if (delta > 0){ //增广成功 e[i].w -= delta; e[i ^ 1].w += delta; return delta; //向上传递 } } } return 0; //没有增广路,返回0 } int dinic(){ //dinic主体 int res = 0; while (bfs(s,t)){ while (int delta = dfs(s,inf)) res += delta; } return res; } int main() { ios::sync_with_stdio(false); cin >> n >> m; //顶点数和边数 init(); int a, b, c; for (int i = 0; i < m; i++){ cin >> a >> b >> c; add_edge(a, b, c); add_edge(b, a, 0); //注意反向边初始为0 } cout << dinic() << endl; return 0; }
dinic算法+当前弧优化
当前弧优化是在dfs找增广路中增加cur[]数组记录下一次寻找的边,而不用每一次都从第一条边开始找
注意:每次分完层都要对cur[]数组初始化
#include<iostream> #include<queue> #include<algorithm> #include<cstdio> using namespace std; const int maxn = 220; const int inf = 0x7fffffff; struct Edge{ int to, w, next; }; Edge e[maxn * 2]; int depth[maxn]; //顶点的层数 int head[maxn]; //实现链式前向星 int cur[maxn]; //当前弧优化,cur就是记录当前点u循环到了哪一条边 int n, m, s, t; int cnt; //边的编号 void init(){ cnt = 0; s = 1, t = n; for (int i = 0; i <= n; i++) head[i] = -1; } void add_edge(int u, int v, int w){ //加边 e[cnt].to = v; e[cnt].w = w; e[cnt].next = head[u]; head[u] = cnt++; } int bfs(int s,int t){ //分层 queue<int> q; for (int i = 0; i <= n; i++) depth[i] = inf; q.push(s); depth[s] = 0; while (!q.empty()){ int p = q.front(); q.pop(); for (int i = head[p]; ~i; i = e[i].next){ //遍历以p为起始的边 if (e[i].w>0 && depth[p]+1<depth[e[i].to]){ //如果是可行边,并且下一个点的深度和当前点的深度相差大于1 depth[e[i].to] = depth[p] + 1; //更新下一个点的深度 q.push(e[i].to); } } } return depth[t] != inf; } int dfs(int u,int flow){ //多次增广 当前点u和flow为当前流量,一开始假设为inf if (u == t) //如果找到汇点 return flow; for (int& i = cur[u]; ~i; i = e[i].next){ //----------修改部分 if (e[i].w > 0 && depth[u] + 1 == depth[e[i].to]){ int delta = dfs(e[i].to, min(flow,e[i].w)); //向下增广 if (delta > 0){ //增广成功 e[i].w -= delta; e[i ^ 1].w += delta; return delta; //向上传递 } } } return 0; //没有增广路,返回0 } int dinic(){ //dinic主体 int res = 0; while (bfs(s,t)){ for (int i = 0; i <= n; i++) cur[i] = head[i]; //-------修改部分,注意每次初始化cur while (int delta = dfs(s,inf)) res += delta; } return res; } int main() { ios::sync_with_stdio(false); cin >> n >> m; //顶点数和边数 init(); int a, b, c; for (int i = 0; i < m; i++){ cin >> a >> b >> c; add_edge(a, b, c); add_edge(b, a, 0); //注意反向边初始为0 } cout << dinic() << endl; return 0; }

浙公网安备 33010602011771号