网络流之最大流-Ford-Fullkerson算法 DFS && BFS

关于理论,强推这个帖子:网络流(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
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值