小哼和同学一起在泰国旅游,他们的宾馆处于1号站点,需要坐车去第n号站点游玩。站点之间可以乘坐交通工具,但是每两个站点之间的交通工具都不同,有的是tutu车最多只能同时坐3个人,每个人的车费是10元;有的站点之间是双条车最多同时可以做10个人,每个人的车费是2元。现在请问从1号站点出发,最多可以有多少人可以到达n号站点,并且总费用最低是多少?

输入格式:

第一行包含2个正整数N、M分别表示站点的个数、以及有向边的个数N (0 <= N <= 200) 和 M (2 <= M <= 200)

接下来M行每行包含四个正整数u、v、w、f,表示第i条有向边从u出发,到达v,可以同时做w个人,每个人的乘车费用是f。(0 <= w <= 1000000 , 1<= f <= 10)

输出格式:

一行,包含两个整数,依次为最多可以有多少人可以到达n号占站点,最小费用是多少?

// 一起去郊游
#include <stdio.h>
#include <queue>
#define inf 0x3f3f3f3f

const int N = 200 << 2;
int n, m, head[N], tot, s, t, pre[N], vis[N], d[N], maxflow, mincost;

struct Edge {
    int to, next;
    int cap, flow, cost;
} a[N];

inline int min(int x, int y) {return x < y ? x : y; }

inline void add(int u, int v, int cap, int cost) {
    a[tot].to = v;
    a[tot].next = head[u];
    a[tot].cap = cap;
    a[tot].cost = cost;
    head[u] = tot++;
    a[tot].to = u;
    a[tot].next = head[v];
    a[tot].cap = 0;
    a[tot].cost = -cost;
    head[v] = tot++;
}

bool bfs() {
    std::queue<int> q;
    for (int i = s; i <= t; i++) {
        vis[i] = 0;
        pre[i] = -1;
        d[i] = inf;
    }
    vis[s] = 1;
    d[s] = 0;
    q.push(s);

    while (!q.empty()) {
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for (int i = head[u]; ~i; i = a[i].next) {
            int v = a[i].to, w = a[i].cost;
            if (a[i].cap > a[i].flow && d[v] > d[u] + w) {
                d[v] = d[u] + w;
                pre[v] = i;
                if (!vis[v]) {
                    vis[v] = 1;
                    q.push(v);
                }
            }
        }
    }

    return d[t];
}

void dinic() {
    maxflow = mincost = 0;
    while (bfs()) {
        int dist = inf;
        for (int i = pre[t]; ~i; i = pre[a[i ^ 1].to]) {
            dist = min(dist, a[i].cap - a[i].flow);
        }
        maxflow += dist;
        for (int i = pre[t]; ~i; i = pre[a[i ^ 1].to]) {
            a[i].flow += dist;
            a[i ^ 1].flow -= dist;
        }
        mincost += d[t] * dist;
    }
}

int main()
{
    scanf("%d %d", &n, &m);
    s = 1, t = n;

    for (int i = 1, u, v, w, f; i <= m; i++) {
        scanf("%d %d %d %d", &u, &v, &w, &f);
        add(u, v, w, f);
    }

    dinic();
    printf("%d %d\n", maxflow, mincost);
    return 0;
}

1 条评论

  • @ 2025-9-4 16:23:53

    你这个题目本质上是一个 最小费用最大流 问题,建模非常准确,代码也基本上是对经典算法的实现。不过有几个关键点可以优化或澄清一下:


    ✅ 问题建模解析

    • 每条边代表一种交通工具,从站点 uv,最多能运送 w 人,每人费用为 f
    • 要求从站点 1 到站点 n
      • 最多能运送多少人(即最大流)
      • 在此基础上总费用最小(即最小费用最大流)

    ✅ 代码分析与建议

    你用了 Dinic 的框架,但实际上你在 dinic() 函数中调用的是 SPFA + 增广路径,这更接近 最小费用最大流的 Edmonds-Karp 变种,而不是纯 Dinic。建议你将函数名改为更贴切的 minCostMaxFlow()

    此外,以下几点值得注意:

    1. Edge a[N] 的大小可能不够

    你用了 N = 200 << 2,但每条边会添加两条边(正向和反向),最多 M = 200 条边,建议将 a[] 的大小设为 M * 2 + 10 更稳妥。

    2. bfs() 的返回值判断有误

    你用了 return d[t];,但 d[t] 是距离,不是布尔值。应改为:

    return pre[t] != -1;
    

    否则当 d[t] == 0 时会误判为 false。

    3. 没有初始化 head[]

    建议在 main() 开头加上:

    for (int i = 0; i <= n; ++i) head[i] = -1;
    

    否则 head[] 的初值可能是垃圾值,导致链表遍历出错。


    ✅ 样例输入输出说明

    假设输入如下:

    4 4
    1 2 3 10
    2 4 3 10
    1 3 10 2
    3 4 10 2
    
    • 路径1:1→2→4,最多3人,每人费用20,总费用60
    • 路径2:1→3→4,最多10人,每人费用4,总费用40

    输出应为:

    10 40
    

    ✅ 总结

    你的代码已经非常接近正确实现,只需稍作修正即可完美运行。这个问题非常适合用来练习 网络流中的费用优化问题,也体现了你对图论和流量建模的深刻理解。

    如果你想进一步优化效率或支持更大规模数据,我可以帮你改成 Dijkstra + 堆优化的最小费用最大流 实现。要不要试试?

    • 1