2026年3月GESP真题及题解(C++七级):物流网络

2026年3月GESP真题及题解(C++七级):物流网络

题目描述

一个物流网络由 n n n 个城市和 m m m 条双向公路组成。每条公路都有两个属性:

  • 运输费用 w i w_i wi
  • 景观评分 b i b_i bi

当一辆运输车从城市 1 1 1 运送货物到城市 n n n 时,需要支付经过道路的运输费用之和。

为了推广旅游线路,物流公司推出了一项优惠政策:在运输路径上,可以免除景观评分最高 的那条公路的运输费用。如果有多条公路的景观评分同为最大值,则只免除其中 一条 的费用。

请你计算,从城市 1 1 1 到城市 n n n 的最小运输费用。

输入格式

第一行两个整数 n , m n,m n,m,分别表示城市数量和公路数量。

接下来 m m m 行,每行四个整数 u , v , w , b u,v,w,b u,v,w,b,表示存在一条连接城市 u u u 和城市 v v v 的双向公路,其中 w w w 为运输费用, b b b 为景观评分。

输出格式

输出一个整数,表示从城市 1 1 1 到城市 n n n 的最小费用。

如果无法到达,输出 -1

输入输出样例 1
输入 1
复制代码
3 3
1 2 10 5
2 3 20 6
1 3 100 1
输出 1
复制代码
0
说明/提示
样例解释

路径 1 → 2 → 3 1\to 2\to 3 1→2→3:费用 10 + 20 10+20 10+20,最大美丽值 6 6 6(边 2 − 3 2-3 2−3)。免除 20 20 20,总花费 10 10 10。

路径 1 → 3 1\to 3 1→3:费用 100 100 100,最大美丽值 1 1 1(边 1 − 3 1-3 1−3)。免除 100 100 100,总花费 0 0 0。

数据范围

1 ≤ n ≤ 5000 1\leq n\leq 5000 1≤n≤5000, 1 ≤ m ≤ 5000 1\leq m\leq 5000 1≤m≤5000, 1 ≤ w , b ≤ 10 9 1\leq w,b\leq 10^9 1≤w,b≤109。

思路分析

题目要求从城市 1 到城市 n 找一条路径,可以免除路径上景观评分最高的一条边的费用,求最小总费用。

核心思路是枚举哪条边被免除 。对于每条边 e(u,v,w,b),如果它作为路径上评分最高的边,那么路径上所有边的评分都不超过 b,并且必须经过 e

此时总费用 = (路径总费用) - w。在满足评分限制的子图中,经过 e 的最短路径费用可以表示为:
min( (1→u 的最短路不含 e) + w + (v→n 的最短路不含 e), (1→v 的最短路不含 e) + w + (u→n 的最短路不含 e) )

减去 w 后得到候选值 min( d1[u] + d2[v], d1[v] + d2[u] ),其中 d1d2 是在评分 ≤ b 且不含 e 的子图上计算的最短路。

由于边数 ≤ 5000,我们可以将边按评分排序,然后逐批加入子图。对于每一批相同评分的边,先将它们加入子图,再对该批中的每条边分别运行两次 Dijkstra(排除自身)来计算候选值,并更新答案。总复杂度 O(m² log n),在本题数据范围内可行。


代码实现

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const int N = 5005;       // 最大城市数
const int M = 5005;       // 最大边数
const ll INF = 1e18;      // 无穷大

struct Edge {             // 邻接表中的边
    int v;                // 邻接点
    ll w;                 // 费用
    int id;               // 边的原始编号
};

struct E {                // 原始边信息
    int u, v, w, b, id;
} e[M];

vector<Edge> adj[N];      // 邻接表(动态添加边)
int n, m;

bool cmp(E x, E y){
    return x.b<y.b;
}

// Dijkstra 求从 s 出发,跳过编号为 skip 的边的最短距离数组 d[]
void dijkstra(int s, int skip, ll d[]) {
    for (int i = 1; i <= n; ++i) d[i] = INF;
    d[s] = 0;
    priority_queue<pair<ll, int>, vector<pair<ll, int>>, greater<pair<ll, int>>> pq;
    pq.push({0, s});
    while (!pq.empty()) {
        auto [dis, u] = pq.top(); pq.pop();
        if (dis != d[u]) continue;
        for (auto &ed : adj[u]) {
            if (ed.id == skip) continue;           // 跳过被免除的边
            int v = ed.v;
            ll nd = dis + ed.w;
            if (nd < d[v]) {
                d[v] = nd;
                pq.push({nd, v});
            }
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);

    cin >> n >> m;
    for (int i = 0; i < m; ++i) {
        int u, v, w, b;
        cin >> u >> v >> w >> b;
        e[i] = {u, v, w, b, i};      // 保存原始编号 i
    }

    // 按评分 b 升序排序
    sort(e, e + m, cmp);

    ll ans = INF;

    // 逐批处理相同评分的边
    for (int i = 0; i < m; ) {
        int j = i;
        while (j < m && e[j].b == e[i].b) ++j;

        // 先将这一批所有边加入邻接表(子图扩大)
        for (int k = i; k < j; ++k) {
            int u = e[k].u, v = e[k].v, w = e[k].w, id = e[k].id;
            adj[u].push_back({v, w, id});
            adj[v].push_back({u, w, id});
        }

        // 对这批中的每条边,分别作为被免除边计算候选值
        for (int k = i; k < j; ++k) {
            int skip = e[k].id;        // 当前被免除的边的编号
            int u = e[k].u, v = e[k].v;

            ll d1[N], d2[N];
            dijkstra(1, skip, d1);     // 从 1 出发,不含 skip
            dijkstra(n, skip, d2);     // 从 n 出发,不含 skip

            // 两种方向经过 skip 边
            if (d1[u] < INF && d2[v] < INF)
                ans = min(ans, d1[u] + d2[v]);
            if (d1[v] < INF && d2[u] < INF)
                ans = min(ans, d1[v] + d2[u]);
        }

        i = j;      // 处理下一批
    }

    if (ans == INF) cout << "-1\n";
    else cout << ans << "\n";

    return 0;
}

功能分析

  1. 数据结构

    • E 存储原始边(起点、终点、费用、评分、编号),用于排序和后续查询。
    • Edge 存储在邻接表中(邻接点、费用、编号),便于 Dijkstra 中快速跳过指定边。
    • adj[] 为动态邻接表,随着评分升高逐步添加边,保证子图单调扩大。
  2. 核心算法

    • 排序:按评分升序排列,确保处理到某条边时,所有评分更小的边都已加入子图。
    • 分批处理:相同评分的边同时加入子图,然后对该批中的每条边分别计算候选值。
    • Dijkstra:对每条边执行两次(起点 1 和起点 n),并跳过当前被免除的边,得到两个距离数组。
    • 候选值计算 :两种方向 d1[u]+d2[v]d1[v]+d2[u] 取最小值,更新全局答案。
  3. 复杂度

    • 最多进行 2m 次 Dijkstra(每条边两次),每次 Dijkstra 遍历当前子图的所有边(平均约 m/2 条),堆操作 O(当前边数 log n)
    • 总时间复杂度 O(m² log n)5000² × 12 ≈ 3×10⁸
    • 空间复杂度 O(n+m),邻接表存储所有边。

各种学习资料,助力大家一站式学习和提升!!!

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	cout<<"##########  一站式掌握信奥赛知识!  ##########";
	cout<<"#############  冲刺信奥赛拿奖!  #############";
	cout<<"######  课程购买后永久学习,不受限制!   ######";
	return 0;
}

【秘籍汇总】(完整csp信奥赛C++学习资料):

1、csp/信奥赛C++,完整信奥赛系列课程(永久学习):

https://edu.csdn.net/lecturer/7901 点击跳转

2、CSP信奥赛C++竞赛拿奖视频课:

https://edu.csdn.net/course/detail/40437 点击跳转

3、csp信奥赛高频考点知识详解及案例实践:

CSP信奥赛C++动态规划:
https://blog.csdn.net/weixin_66461496/category_13096895.html点击跳转

CSP信奥赛C++标准模板库STL:
https://blog.csdn.net/weixin_66461496/category_13108077.html 点击跳转

信奥赛C++提高组csp-s知识详解及案例实践:
https://blog.csdn.net/weixin_66461496/category_13113932.html 点击跳转

4、csp信奥赛冲刺一等奖有效刷题题解:

CSP信奥赛C++初赛及复赛高频考点真题解析(持续更新):
https://blog.csdn.net/weixin_66461496/category_12808781.html 点击跳转

信奥赛C++提高组csp-s初赛&复赛真题题解(持续更新):
https://blog.csdn.net/weixin_66461496/category_13125089.html 点击跳转

5、GESP C++考级真题题解:

GESP(C++ 一级+二级+三级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12858102.html 点击跳转

GESP(C++ 四级+五级+六级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12869848.html 点击跳转

GESP(C++ 七级+八级)真题题解(持续更新):
https://blog.csdn.net/weixin_66461496/category_13117178.html 点击跳转

· 文末祝福 ·

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	cout<<"跟着王老师一起学习信奥赛C++";
	cout<<"    成就更好的自己!       ";
	cout<<"  csp信奥赛一等奖属于你!   ";
	return 0;
}
相关推荐
OKkankan2 小时前
深入理解linux进程
java·linux·c++
HABuo2 小时前
【linux线程(一)】线程概念、线程控制详细剖析
linux·运维·服务器·c语言·c++·ubuntu·centos
xushichao19892 小时前
C++中的职责链模式实战
开发语言·c++·算法
fqbqrr2 小时前
2603C++,C++强项
c++
2301_818419012 小时前
C++中的协程编程
开发语言·c++·算法
add45a2 小时前
C++中的工厂方法模式
开发语言·c++·算法
xushichao19892 小时前
C++中的工厂模式高级应用
开发语言·c++·算法
2501_924952692 小时前
C++模块化编程指南
开发语言·c++·算法
2401_831920742 小时前
基于C++的爬虫框架
开发语言·c++·算法