关于理论,强推这个帖子:网络流(2)——用Ford-Fullkerson算法寻找最大流
代码实现
DFS
/******************************************
* @Author : 鱼香肉丝没有鱼
* @Date : 2021-10-30 15:58:21
* @LastEditors : 鱼香肉丝没有鱼
* @LastEditTime : 2021-11-16 10:06:25
******************************************/
#include <algorithm>
#include <cstring>
#include <iostream>
#include <stdio.h>
#include <vector>
using namespace std;
const int max_v = 1000;
const int INF = 0x3f3f3f3f;
struct edge
{
int to; //流向的节点
int cap; // capacity 流量
int rev; //反向边的编号,从G[i]指向to的第rev条边,
// 因为读入一条流量路劲时也会建立反向边,所以从to流出的数量和序号是一样的
};
// G[i]保存的是节点i向外流出的边的集合,是一个数组,包含了增广路径
// 并不是每一个位置都被使用,只有有路径(有指出)的才会存储信息
vector<edge> G[max_v];
bool used[max_v]; //是否使用
// 注意区分流量和容量
//当前节点;终点;容量,返回值是路径的容量,也就是允许的最大流量
int dfs(int v, int t, int cap)
{
if(v == t) //到达汇点
return cap;
used[v] = 1; //标记这个节点进入了路径
// G[v].size()等于从这个节点流出的路径数量,所以对于某个节点要遍历所有的路径
for(int i = 0; i < G[v].size(); i++) {
edge& tmp = G[v][i]; //取出这个节点进行分析
// !used[tmp.to]是为了保持简单路径,其实不是简单路径也可以,只是需要更多时间
// 简单路径就是一个节点只经过一次
// tmp.cap>0 还剩下容量可以测试
if(!used[tmp.to] && tmp.cap > 0) {
//让当前节点的流向终点进入下一层递归
// min(cap,tmp.cap),容量大小取决于小的那个,即瓶颈
int tmpCap = dfs(tmp.to, t, min(cap, tmp.cap));
// 返回值其实是这条路径中允许通过的最大流量,也就是路径中的边里最小的那个容量
if(tmpCap > 0) { //如果>0说明可以使用
tmp.cap -= tmpCap; //正向边容量缩减,被使用了一部分
G[tmp.to][tmp.rev].cap += tmpCap; //反向边也就是增广路径中的边流量增加
return tmpCap; //返回这个容量,这就是目前遍历的节点组成的路径允许通过的最大流量
}
}
}
return 0;
}
int main()
{
freopen("file in.txt", "r", stdin);
int s, t, m; //源点;汇点;路径数量
scanf("%d %d %d", &s, &t, &m);
int from, to, cap; //从from节点流向to节点,容量cap
for(int i = 0; i < m; i++) { // m个节点
scanf("%d %d %d", &from, &to, &cap);
//存到from这个节点流出路径结合里面,G[to].size()流向to的第几条边
G[from].push_back((edge){to, cap, G[to].size()});
// 反向边的初始化,流量为0;G[from].size()-1其实是数组大小和路径序号的一个规律
G[to].push_back((edge){from, 0, G[from].size() - 1});
}
int flow = 0;
while(1) { //不断的重复这个dfs,每次都从源点开始,最终到达终点
memset(used, 0, sizeof(used)); //用0初始化之后全部是false
int tmpflow = dfs(s, t, INF); //初始的时候容量为无穷大
if(tmpflow == 0) //当没有可用路径的时候退出
break;
flow += tmpflow;
}
printf("%d\n", flow);
return 0;
}
/*
1 4 7
1 2 10
1 5 2
2 5 6
2 3 6
3 4 8
3 5 3
5 4 5
结果:11
*/
BFS
/******************************************
* @Author : 鱼香肉丝没有鱼
* @Date : 2021-10-30 15:58:21
* @LastEditors : 鱼香肉丝没有鱼
* @LastEditTime : 2021-11-16 10:06:25
******************************************/
#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
#include <stdio.h>
#include <vector>
using namespace std;
const int max_v = 1000;
const int INF = 0x3f3f3f3f;
struct edge
{
int to; //流向的节点
int cap; // capacity 流量
int rev; //反向边的编号,从G[i]指向to的第rev条边,
// 因为读入一条流量路劲时也会建立反向边,所以从to流出的数量和序号是一样的
};
// G[i]保存的是节点i向外流出的边的集合,是一个数组,包含了增广路径
// 并不是每一个位置都被使用,只有有路径(有指出)的才会存储信息
vector<edge> G[max_v];
int step[max_v]; //从源点到节点i的距离
int iter[max_v]; //节点i的第几条边开始有用
// bfs的作用就是寻找剩余路径并且在step中体现,如果没有路径了,step就不能更新完全,
void bfs(int s)
{
memset(step, -1, sizeof(step)); //初始距离设为-1
step[s] = 0; //源点到源点的距离为0,同时也说明被使用了这个节点
queue<int> q;
q.push(s);
while(!q.empty()) {
int curr = q.front(); //取出当前节点
q.pop();
for(int i = 0; i < G[curr].size(); i++) {
edge& tmp = G[curr][i];
// 容量不为0而且没有使用过
if(tmp.cap > 0 && step[tmp.to] < 0) {
step[tmp.to] = step[curr] + 1; //做好标记同时也存储了距离
q.push(tmp.to); //放入队列中
}
}
}
}
// 注意区分流量和容量
//当前节点;终点;容量,返回值是路径的容量,也就是允许的最大流量
int dfs(int v, int t, int cap)
{
if(v == t) //到达汇点
return cap;
// G[v].size()等于从这个节点流出的路径数量,所以对于某个节点要遍历所有的路径
//这里是引用,i++的同时iter也++,其实相当于上个的used,不过不用判断了
for(int& i = iter[v]; i < G[v].size(); i++) {
edge& tmp = G[v][i]; //取出这个节点进行分析
// 简单路径就是一个节点只经过一次
// tmp.cap>0 还剩下容量可以测试
// step[tmp.to] > step[v],在bfs中是累加的,这个判断便不会倒回去,保证dfs向终点方向推进
if(step[tmp.to] > step[v] && tmp.cap > 0) {
//让当前节点的流向终点进入下一层递归
// min(cap,tmp.cap),容量大小取决于小的那个,即瓶颈
int T_cap = dfs(tmp.to, t, min(cap, tmp.cap));
// 返回值其实是这条路径中允许通过的最大流量,也就是路径中的边里最小的那个容量
if(T_cap > 0) { //如果>0说明可以使用
tmp.cap -= T_cap; //正向边容量缩减,被使用了一部分
G[tmp.to][tmp.rev].cap += T_cap; //反向边也就是增广路径中的边流量增加
return T_cap; //返回这个容量,这就是目前遍历的节点组成的路径允许通过的最大流量
}
}
}
return 0;
}
//不断的寻找新路径,然后把新路径中的容量累加到flow里面
int max_flow(int s, int t)
{
int flow = 0;
while(1) {
bfs(s); //寻找剩余路径
if(step[t] < 0) //如果走不通,即找不到路径了,那么step[t]就不会更新,还是-1
return flow;
memset(iter, 0, sizeof(iter));
int T_flow; //新路径的容量
while((T_flow = dfs(s, t, INF)) > 0)
flow += T_flow;
}
}
int main()
{
freopen("file in.txt", "r", stdin);
int s, t, m; //源点;汇点;路径数量
scanf("%d %d %d", &s, &t, &m);
int from, to, cap; //从from节点流向to节点,容量cap
for(int i = 0; i < m; i++) { // m个节点
scanf("%d %d %d", &from, &to, &cap);
//存到from这个节点流出路径结合里面,G[to].size()流向to的第几条边
G[from].push_back((edge){to, cap, G[to].size()});
// 反向边的初始化,流量为0;G[from].size()-1其实是数组大小和路径序号的一个规律
G[to].push_back((edge){from, 0, G[from].size() - 1});
}
printf("%d\n", max_flow(s, t));
return 0;
}
/*
1 4 7
1 2 10
1 5 2
2 5 6
2 3 6
3 4 8
3 5 3
5 4 5
结果:11
*/