猫和老鼠
给定包含猫窝和老鼠洞的带权无向图,定义安全节点为老鼠能从此节点出发规划一条逃往老鼠洞的路径,且路径上任一节点处猫的全局最短到达时间都严格大于老鼠沿该路径到达的时间,要求求出所有安全节点上的奶酪价值之和。

猫和老鼠所在的庄园可以视为一张由 个点和 条带权无向边构成的连通图。结点依次以 编号,结点 ()有价值为 的奶酪。在 条带权无向边中,第 ()条无向边连接结点 与结点 ,边权 表示猫和老鼠通过这条边所需的时间。
猫窝位于结点 ,老鼠洞位于结点 。对于老鼠而言,结点 是安全的当且仅当:
- 老鼠能规划一条从结点 出发逃往老鼠洞的路径,使得对于路径上任意结点 (包括结点 与老鼠洞)都有:猫从猫窝出发到结点 的最短时间严格大于老鼠从结点 沿这条路径前往结点 所需的时间。
老鼠在拿取安全结点的奶酪时不存在被猫抓住的可能,但在拿取不是安全结点的奶酪时则不一定。为了确保万无一失,老鼠决定只拿取安全结点放置的奶酪。请你计算老鼠所能拿到的奶酪价值之和。
输入格式
第一行,两个正整数 ,分别表示图的结点数与边数。
第二行,两个正整数 ,分别表示猫窝的结点编号,以及老鼠洞的结点编号。
第三行, 个正整数 ,表示各个结点的奶酪价值。
接下来 行中的第 行()包含三个正整数 ,表示图中连接结点 与结点 的边,边权为 。
输出格式
输出一行,一个整数,表示老鼠所能拿到的奶酪价值之和。
输入样例 1
5 51 21 2 4 8 161 2 42 3 33 4 12 5 23 1 8输出样例 1
22输入样例 2
6 103 41 1 1 1 1 11 2 62 3 33 1 43 4 54 5 85 6 26 4 13 2 45 4 43 3 6输出样例 2
3数据范围
对于 的测试点,保证 ,。
对于所有测试点,保证 ,, 且 ,,。
这道题本质上是一道极其精妙的图论与数学变形题。看似复杂的路径动态判定,推导后会发现一个非常惊艳的等价条件。
一个结点 是安全的,当且仅当:老鼠从 到老鼠洞 的最短距离,严格小于猫从猫窝 到老鼠洞 的最短距离。
即:
只需以老鼠洞 为起点跑一次 Dijkstra 单源最短路,即可在 的时间内解决全题!
根据题目定义,结点 安全,意味着存在一条从 到 的路径 ,使得对于 上的任意结点 ,都有:
其中 表示老鼠沿路径 从 走到 所需的时间; 表示猫从猫窝 到 的图上最短时间。
- :结点 到 的最短路径长度(满足三角不等式:)。
- :猫从猫窝 到 的最短时间。
- :老鼠沿路径 从 走到 的实际耗时。
- 安全定义:存在路径 (起点 ,终点 ),使得 ,均有 。
若 安全,则存在一条合法逃跑路径 。直接取路径 的终点 ,代入定义得:
结合最短路性质 ,由传递性可得:
已知 ,构造路径 为 到 的图上最短路径。
对于 上的任意结点 :
- 最短路分解:。
- 三角不等式:。
利用已知条件 进行放缩:
即对 上的任意结点 均满足 。说明直接走最短路径 即可安全逃脱,故 必为安全结点。
- 将无向图建立邻接表(注意边权可达 ,距离需用
long long存储)。 - 以老鼠洞 为起点,运行一次 Dijkstra 算法,求出 到所有结点 的最短路 。
- 由于无向图的对称性, 即为 到 的最短距离(即猫到老鼠洞的时间)。
- 遍历每个结点 ():
- 若 ,则 是安全结点,累加奶酪价值 。
- 输出总价值。
#include <iostream>#include <vector>#include <queue>
using namespace std;
const long long INF = 1e18; // 边权最大 10^9,距离可能达到 10^14,需开 long long
struct Edge { int to; long long w;};
int main() { // 加速 I/O ios_base::sync_with_stdio(false); cin.tie(NULL);
int n, m; if (!(cin >> n >> m)) return 0;
int a, b; cin >> a >> b;
vector<long long> c(n + 1); for (int i = 1; i <= n; ++i) { cin >> c[i]; }
vector<vector<Edge>> adj(n + 1); for (int i = 0; i < m; ++i) { int u, v; long long w; cin >> u >> v >> w; adj[u].push_back({v, w}); adj[v].push_back({u, w}); }
// 以老鼠洞 b 为起点跑单源最短路 vector<long long> dist(n + 1, INF); priority_queue<pair<long long, int>, vector<pair<long long, int>>, greater<pair<long long, int>>> pq;
dist[b] = 0; pq.push({0, b});
while (!pq.empty()) { auto [d, u] = pq.top(); pq.pop();
if (d > dist[u]) continue;
for (const auto& edge : adj[u]) { if (dist[u] + edge.w < dist[edge.to]) { dist[edge.to] = dist[u] + edge.w; pq.push({dist[edge.to], edge.to}); } } }
// 猫到老鼠洞的时间 d_cat_b 等于 dist[a] long long d_cat_b = dist[a];
// 统计安全结点的奶酪价值和 long long total_cheese = 0; for (int u = 1; u <= n; ++u) { if (dist[u] < d_cat_b) { total_cheese += c[u]; } }
cout << total_cheese << "\n";
return 0;}- 时间复杂度:。只需跑一次优先队列优化的 Dijkstra 算法,完全轻松应对 的数据规模。
- 空间复杂度:,用于保存图的邻接表与最短路数组。