所有奶牛都要判断能否在 M 秒内到达同一个目标点 1,所以只需从 1 号草地做一次 Dijkstra,再按奶牛编号检查距离是否不超过 M。
OJ: luogu
题目 ID: P6770
难度:普及/提高-
标签:最短路图论堆
日期: 2026-06-20 03:49
题意
农场有 F 片草地,1 号草地上是被盗的谷仓。
卫星拍下了偷窃前 M 秒时,每头奶牛所在的位置。
如果某头奶牛能在 M 秒内从照片中的位置赶到 1 号草地,那它就有作案嫌疑。
要求输出:
- 有嫌疑的奶牛数量
- 这些奶牛的编号(按输入顺序编号,从
1开始)
思路
先看一个最直接的小数据暴力:
cpp
// brute.cpp:用 Floyd 求 1 号草地到所有点的最短路,再检查每头牛是否能在 M 秒内到达。
// 只适合小数据,但更贴近题意。
#include <bits/stdc++.h>
using namespace std;
const int MAXF = 105;
const long long INF = (1LL << 60);
int f, p, c, m_limit;
int cow_pos[105];
long long dist_arr[MAXF][MAXF];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> f >> p >> c >> m_limit;
for (int i = 1; i <= f; i++) {
for (int j = 1; j <= f; j++) {
if (i == j) {
dist_arr[i][j] = 0;
}
else {
dist_arr[i][j] = INF;
}
}
}
for (int i = 1; i <= p; i++) {
int u, v, len;
cin >> u >> v >> len;
if (len < dist_arr[u][v]) {
dist_arr[u][v] = len;
dist_arr[v][u] = len;
}
}
for (int i = 1; i <= c; i++) {
cin >> cow_pos[i];
}
for (int k = 1; k <= f; k++) {
for (int i = 1; i <= f; i++) {
for (int j = 1; j <= f; j++) {
if (dist_arr[i][k] + dist_arr[k][j] < dist_arr[i][j]) {
dist_arr[i][j] = dist_arr[i][k] + dist_arr[k][j];
}
}
}
}
vector<int> answer;
for (int i = 1; i <= c; i++) {
if (dist_arr[1][cow_pos[i]] <= m_limit) {
answer.push_back(i);
}
}
cout << answer.size() << '\n';
for (size_t i = 0; i < answer.size(); i++) {
cout << answer[i] << '\n';
}
return 0;
}暴力做法是 Floyd:
- 先求任意两点最短路
- 再看每头奶牛所在位置到
1号草地的距离是否不超过M
这个做法能帮助理解题意,但这题并不需要全源最短路。
关键观察很简单:
- 所有奶牛都要判断“能不能到同一个点
1”
也就是说,真正需要的是:
1号草地到所有草地的最短路
因为图是无向图,所以:
这样只要从 1 号草地做一次 Dijkstra,就能得到每头奶牛需要的答案。
流程就是:
- 建无向带权图
- 从
1号点做一次单源最短路 - 顺序检查每头奶牛所在位置
cow_pos[i] - 如果
dist[cow_pos[i]] <= M,就把这头奶牛编号加入答案
代码
cpp
#include <bits/stdc++.h>
using namespace std;
const int MAXF = 500 + 5;
const int MAXP = 1000 * 2 + 5;
const long long INF = (1LL << 60);
struct HeapNode {
int u;
long long dist;
bool operator < (const HeapNode &other) const {
return dist > other.dist;
}
};
int f, p, c, m_limit;
int head[MAXF], to[MAXP], nxt[MAXP], w[MAXP], edge_cnt;
int cow_pos[105];
long long dist_arr[MAXF];
bool vis[MAXF];
void init_graph() {
edge_cnt = 0;
for (int i = 1; i <= f; i++) {
head[i] = 0;
}
}
void add_edge(int u, int v, int len) {
edge_cnt++;
to[edge_cnt] = v;
w[edge_cnt] = len;
nxt[edge_cnt] = head[u];
head[u] = edge_cnt;
}
void dijkstra(int start) {
for (int i = 1; i <= f; i++) {
dist_arr[i] = INF;
vis[i] = false;
}
priority_queue<HeapNode> pq;
dist_arr[start] = 0;
pq.push({start, 0});
while (!pq.empty()) {
HeapNode cur = pq.top();
pq.pop();
int u = cur.u;
if (vis[u]) {
continue;
}
vis[u] = true;
for (int i = head[u]; i != 0; i = nxt[i]) {
int v = to[i];
long long nd = dist_arr[u] + w[i];
if (nd < dist_arr[v]) {
dist_arr[v] = nd;
pq.push({v, nd});
}
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> f >> p >> c >> m_limit;
init_graph();
for (int i = 1; i <= p; i++) {
int u, v, len;
cin >> u >> v >> len;
add_edge(u, v, len);
add_edge(v, u, len);
}
for (int i = 1; i <= c; i++) {
cin >> cow_pos[i];
}
// 1 号草地是被盗谷仓,只需要从这里做一次单源最短路。
dijkstra(1);
vector<int> answer;
for (int i = 1; i <= c; i++) {
if (dist_arr[cow_pos[i]] <= m_limit) {
answer.push_back(i);
}
}
cout << answer.size() << '\n';
for (size_t i = 0; i < answer.size(); i++) {
cout << answer[i] << '\n';
}
return 0;
}复杂度
一次堆优化 Dijkstra:
再顺序检查所有奶牛:
总复杂度:
空间复杂度:
总结
这题的重点不是最短路模板本身,而是先看清楚查询结构:
- 所有牛都在问“能不能到同一个目标点”
一旦发现这一点,就没必要对每头牛单独跑最短路。
直接做一次以目标点为源点的单源最短路即可。
一图流解析
这张图把本题的建模、关键转移、实现检查和训练方法压缩到一页,适合读完正文后复盘。
