2025年12月GESP真题及题解(C++八级): 猫和老鼠

2025年12月GESP真题及题解(C++八级): 猫和老鼠

题目描述

猫和老鼠所在的庄园可以视为一张由 n n n 个点和 m m m 条带权无向边构成的连通图。结点依次以 1 , 2 , ... , n 1,2,\ldots,n 1,2,...,n 编号,结点 i i i( 1 ≤ i ≤ n 1\le i\le n 1≤i≤n)有价值为 c i c_i ci 的奶酪。在 m m m 条带权无向边中,第 i i i( 1 ≤ i ≤ m 1\le i\le m 1≤i≤m)条无向边连接结点 u i u_i ui 与结点 v i v_i vi,边权 w i w_i wi 表示猫和老鼠通过这条边所需的时间。

猫窝位于结点 a a a,老鼠洞位于结点 b b b。对于老鼠而言,结点 u u u 是安全的当且仅当:

  • 老鼠能规划一条从结点 u u u 出发逃往老鼠洞的路径,使得对于路径上任意结点 x x x(包括结点 u u u 与老鼠洞)都有:猫从猫窝出发到结点 x x x 的最短时间严格大于 老鼠从结点 u u u 沿这条路径 前往结点 x x x 所需的时间。

老鼠在拿取安全结点的奶酪时不存在被猫抓住的可能,但在拿取不是安全结点的奶酪时则不一定。为了确保万无一失,老鼠决定只拿取安全结点放置的奶酪。请你计算老鼠所能拿到的奶酪价值之和。

输入格式

第一行,两个正整数 n , m n,m n,m,分别表示图的结点数与边数。

第二行,两个正整数 a , b a,b a,b,分别表示猫窝的结点编号,以及老鼠洞的结点编号。

第三行, n n n 个正整数 c 1 , c 2 , ... , c n c_1,c_2,\ldots,c_n c1,c2,...,cn,表示各个结点的奶酪价值。

接下来 m m m 行中的第 i i i 行( 1 ≤ i ≤ m 1\le i\le m 1≤i≤m)包含三个正整数 u i , v i , w i u_i,v_i,w_i ui,vi,wi,表示图中连接结点 u i u_i ui 与结点 v i v_i vi 的边,边权为 w i w_i wi。

输出格式

输出一行,一个整数,表示老鼠所能拿到的奶酪价值之和。

输入输出样例 1
输入 1
复制代码
5 5
1 2
1 2 4 8 16
1 2 4
2 3 3
3 4 1
2 5 2
3 1 8
输出 1
复制代码
22
输入输出样例 2
输入 2
复制代码
6 10
3 4
1 1 1 1 1 1
1 2 6
2 3 3
3 1 4
3 4 5
4 5 8
5 6 2
6 4 1
3 2 4
5 4 4
3 3 6
输出 2
复制代码
3
说明/提示

对于 40 % 40\% 40% 的测试点,保证 1 ≤ n ≤ 500 1\le n\le 500 1≤n≤500, 1 ≤ m ≤ 500 1\le m\le 500 1≤m≤500。

对于所有测试点,保证 1 ≤ n ≤ 10 5 1\le n\le 10^5 1≤n≤105, 1 ≤ m ≤ 10 5 1\le m\le 10^5 1≤m≤105, 1 ≤ a , b ≤ n 1\le a,b\le n 1≤a,b≤n 且 a ≠ b a\neq b a=b, 1 ≤ u i , v i ≤ n 1\le u_i,v_i\le n 1≤ui,vi≤n, 1 ≤ c i , w i ≤ 10 9 1\le c_i , w_i\le 10^9 1≤ci,wi≤109。

算法思路

  1. 计算猫的最短时间 :从猫窝 a 出发,使用 Dijkstra 算法计算到每个节点的最短时间 dist_cat[]
  2. 安全节点传播 :从老鼠洞 b 开始,逆向扩展安全节点。定义安全值 H[u] 表示从 ub 的安全路径中,路径上所有节点 xdist_cat[x] 减去老鼠从 ux 的时间的最小值。初始时 H[b] = dist_cat[b]
  3. 扩展条件 :对于当前安全节点 v 和其邻居 u,如果边权 w(u,v) < H[v],则 u 可能安全。计算 u 的候选安全值 cand = min(dist_cat[u], H[v] - w(u,v))。若 cand > 0 且大于 u 当前的安全值,则更新 H[u] 并将 u 加入优先队列。
  4. 求和 :所有 H[i] > 0 的节点均为安全节点,将其奶酪价值相加得到答案。

代码实现

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

typedef long long ll;
const int MAXN = 100010;
const ll INF = 1e18;

int n, m, a, b;
ll c[MAXN];                     // 每个节点的奶酪价值
vector<pair<int, ll>> adj[MAXN]; // 邻接表存储图,pair为(邻居节点, 边权)

ll dist_cat[MAXN]; // 猫从窝点a到每个节点的最短时间
ll H[MAXN];        // H[u]表示节点u的安全值,即从u到b的安全路径中的最小时间差

// 从起点start(猫窝a)运行Dijkstra算法,计算猫到每个节点的最短时间
void dijkstra(int start) {
    for (int i = 1; i <= n; i++) dist_cat[i] = INF;
    dist_cat[start] = 0;
    // 使用最小堆,按距离从小到大处理
    priority_queue<pair<ll, int>, vector<pair<ll, int>>, greater<pair<ll, int>>> pq;
    pq.push({0, start});
    while (!pq.empty()) {
        auto [d, u] = pq.top(); pq.pop();
        if (d != dist_cat[u]) continue; // 跳过过时的记录
        for (auto [v, w] : adj[u]) {
            if (dist_cat[v] > d + w) {
                dist_cat[v] = d + w;
                pq.push({dist_cat[v], v});
            }
        }
    }
}

// 从老鼠洞b开始计算安全节点及其H值
void compute_H() {
    for (int i = 1; i <= n; i++) H[i] = -1; // -1表示尚未确定是否安全
    H[b] = dist_cat[b]; // 老鼠洞b本身是安全的,其H值为猫到b的最短时间
    // 使用最大堆,按H值从大到小处理,以便优先扩展H值大的节点
    priority_queue<pair<ll, int>> pq;
    pq.push({H[b], b});
    while (!pq.empty()) {
        auto [h_val, v] = pq.top(); pq.pop();
        if (h_val != H[v]) continue; // 跳过过时的记录
        // 遍历v的所有邻居u
        for (auto [u, w] : adj[v]) {
            // 如果边权w严格小于v的当前H值,则考虑通过v使u安全
            if (w < h_val) {
                // 计算u的候选H值:min(猫到u的最短时间, H[v] - w)
                ll cand = min(dist_cat[u], h_val - w);
                // 只有候选值大于0才表示u可能安全,并且大于当前记录的H值才更新
                if (cand > 0 && cand > H[u]) {
                    H[u] = cand;
                    pq.push({H[u], u});
                }
            }
        }
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    // 读入数据
    cin >> n >> m;
    cin >> a >> b;
    for (int i = 1; i <= n; i++) cin >> c[i];
    for (int i = 0; i < m; i++) {
        int u, v; ll w;
        cin >> u >> v >> w;
        adj[u].push_back({v, w});
        adj[v].push_back({u, w}); // 无向图,添加双向边
    }
    // 步骤1:计算猫从窝点a到所有节点的最短时间
    dijkstra(a);
    // 步骤2:从老鼠洞b开始计算所有安全节点
    compute_H();
    // 步骤3:累加所有安全节点的奶酪价值
    ll ans = 0;
    for (int i = 1; i <= n; i++) {
        if (H[i] > 0) { // H[i] > 0 表示节点i是安全的
            ans += c[i];
        }
    }
    cout << ans << endl;
    return 0;
}

功能分析

时间复杂度
  • Dijkstra 算法:O((n+m) log n)
  • 安全节点扩展:每个节点和边可能被多次检查,但每个节点最多被更新一次(取最大 H 值),整体 O((n+m) log n)
  • 总复杂度:O((n+m) log n),可处理 n, m ≤ 10 5 10^5 105 的数据规模。
空间复杂度
  • 使用邻接表存储图:O(n+m)
  • 数组 dist_catHcO(n)
  • 优先队列:O(n)
  • 总空间复杂度:O(n+m)
注意事项
  • 使用 long long 存储边权和距离,避免溢出。
  • 安全节点必须满足 H[i] > 0,排除猫窝 a(其 dist_cat[a] = 0)。
  • 扩展时使用最大堆优先处理 H 值大的节点,以最大化安全区域的扩展。

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

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

1、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

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

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

CSP信奥赛C++一等奖通关刷题题单及题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12673810.html 点击跳转

3、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

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

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

· 文末祝福 ·

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main(){
	cout<<"跟着王老师一起学习信奥赛C++";
	cout<<"    成就更好的自己!       ";
	cout<<"  csp信奥赛一等奖属于你!   ";
	return 0;
}
相关推荐
_OP_CHEN2 小时前
【算法基础篇】(四十六)同余方程终极攻略:从基础转化到实战破解
c++·算法·蓝桥杯·数论·同余方程·扩展欧几里得算法·acm/icpc
程序员泡椒4 小时前
二分查找Go版本实现
数据结构·c++·算法·leetcode·go·二分
txinyu的博客10 小时前
解析业务层的key冲突问题
开发语言·c++·分布式
SmartRadio11 小时前
ESP32添加修改蓝牙名称和获取蓝牙连接状态的AT命令-完整UART BLE服务功能后的完整`main.c`代码
c语言·开发语言·c++·esp32·ble
charlie11451419113 小时前
嵌入式的现代C++教程——constexpr与设计技巧
开发语言·c++·笔记·单片机·学习·算法·嵌入式
CSDN_RTKLIB16 小时前
【字符编码】有无BOM的UTF-8
c++
Chary201616 小时前
opengl 学习资料路径
c++·opengl
im_AMBER17 小时前
Leetcode 102 反转链表
数据结构·c++·学习·算法·leetcode·链表
今儿敲了吗17 小时前
01|多项式输出
c++·笔记·算法