题解:洛谷 P3376 【模板】网络最大流

【题目来源】

洛谷:P3376 【模板】网络最大流 - 洛谷

【题目描述】

如题,给出一个网络图,以及其源点和汇点,求出其网络最大流。

【输入】

第一行包含四个正整数 \(n,m,s,t\),分别表示点的个数、有向边的个数、源点序号、汇点序号。

接下来 \(m\) 行每行包含三个正整数 \(u_i,v_i,w_i\),表示第 \(i\) 条有向边从 \(u_i\) 出发,到达 \(v_i\),边权为 \(w_i\)(即该边最大流量为 \(w_i\))。

【输出】

一行,包含一个正整数,即为该网络的最大流。

【输入样例】

4 5 4 3
4 2 30
4 3 20
2 3 20
2 1 30
1 3 30

【输出样例】

50

【算法标签】

《洛谷 P3376 网络最大流》 #网络流# #模板题# #O2优化#

【代码详解】

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 205, M = 5005 * 2;

int n, m, s, t;  // 点数,边数,源点,汇点
// 使用数组模拟邻接表
int h[N], e[M], w[M], ne[M], idx = 2;  // idx从2开始,方便找反向边
int d[N], cur[N];  // d: 层次深度,cur: 当前弧优化数组

// 添加有向边和反向边
void add(int a, int b, int c)
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}

// BFS构建层次图
bool bfs()
{
    memset(d, 0, sizeof(d));
    queue<int> q;
    q.push(s);
    d[s] = 1;
    while (q.size())
    {
        int u = q.front();
        q.pop();
        for (int i = h[u]; i != -1; i = ne[i])
        {
            int v = e[i];
            if (d[v] == 0 && w[i])  // 如果v未访问且边有容量
            {
                d[v] = d[u] + 1;
                q.push(v);
                if (v == t)
                {
                    return true;  // 找到汇点
                }
            }
        }
    }
    return false;  // 无法到达汇点
}

// DFS进行多路增广
int dfs(int u, int mf)
{
    if (u == t)
    {
        return mf;  // 到达汇点
    }
    int sum = 0;
    for (int i = cur[u]; i != -1; i = ne[i])
    {
        cur[u] = i;  // 当前弧优化
        int v = e[i];
        if (d[v] == d[u] + 1 && w[i])  // 满足层次关系和容量限制
        {
            int f = dfs(v, min(mf, w[i]));
            w[i] -= f;     // 正向边减少容量
            w[i ^ 1] += f; // 反向边增加容量
            sum += f;
            mf -= f;
            if (mf == 0)
            {
                break;  // 流量已用完
            }
        }
    }
    if (sum == 0)
    {
        d[u] = 0;  // 残量网络中u不可达
    }
    return sum;
}

// Dinic算法主函数
int dinic()
{
    int flow = 0;
    while (bfs())  // 每次BFS构建新的层次图
    {
        memcpy(cur, h, sizeof(h));  // 重置当前弧
        flow += dfs(s, 1e9);  // 从源点开始增广
    }
    return flow;
}

signed main()
{
    cin >> n >> m >> s >> t;
    memset(h, -1, sizeof(h));
    for (int i = 1; i <= m; i++)
    {
        int u, v, w;
        cin >> u >> v >> w;
        add(u, v, w), add(v, u, 0);  // 添加正向边和反向边
    }
    cout << dinic() << endl;
    return 0;
}

【运行结果】

4 5 4 3
4 2 30
4 3 20
2 3 20
2 1 30
1 3 30
50
posted @ 2026-03-24 14:16  团爸讲算法  阅读(3)  评论(0)    收藏  举报