[USACO09OPEN] Hide and Seek S

GitHub跳转原题关系图返回列表

这是无权图单源最短路。先从 1 号点做 BFS,得到每个点的最短层数,再统计最远距离、最小编号和该距离出现次数。

OJ: luogu

题目 ID: P2951

难度:普及-

标签:最短路图论bfs

日期: 2026-06-20 03:45

题意

给一张无权无向图,农夫在 1 号谷仓。

要求找出:

  1. 距离 1 号谷仓最远的谷仓编号(如果有多个,取编号最小)
  2. 这个最远距离
  3. 距离等于这个最远距离的谷仓个数

思路

先看一个最直接的小数据暴力:

cpp
// brute.cpp:用 Floyd 求 1 号点到所有点的最短距离。
// 只适合小数据,但逻辑最直接。
#include <bits/stdc++.h>
using namespace std;

const int MAXN = 105;
const int INF = 1e9;

int n, m;
int dist_arr[MAXN][MAXN];

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

    cin >> n >> m;

    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (i == j) {
                dist_arr[i][j] = 0;
            }
            else {
                dist_arr[i][j] = INF;
            }
        }
    }

    for (int i = 1; i <= m; i++) {
        int u, v;
        cin >> u >> v;
        dist_arr[u][v] = 1;
        dist_arr[v][u] = 1;
    }

    for (int k = 1; k <= n; k++) {
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= n; 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];
                }
            }
        }
    }

    int best_id = 1;
    int best_dist = -1;
    int count = 0;

    for (int i = 1; i <= n; i++) {
        if (dist_arr[1][i] > best_dist) {
            best_dist = dist_arr[1][i];
            best_id = i;
            count = 1;
        }
        else if (dist_arr[1][i] == best_dist) {
            count++;
        }
    }

    cout << best_id << ' ' << best_dist << ' ' << count << '\n';

    return 0;
}

暴力做法是 Floyd:

  1. 先求任意两点最短路
  2. 只看 1 号点到所有点的距离
  3. 找最大值,并统计最小编号和数量

这个思路很直观,但这题其实根本不需要全源最短路。

因为图是:

  • 无权图
  • 单源 1

这两个信号一出来,就应该直接想到 BFS。

1 号点开始做 BFS 时:

  • 第 0 层是 1
  • 第 1 层是离 1 一条边的点
  • 第 2 层是离 1 两条边的点

因此,BFS 求出来的 dist[i] 就是:

  • 1i 的最短路径边数

接下来只要顺序扫一遍:

  • 如果发现更大的距离,就更新答案
  • 如果发现相同的最大距离,就给数量加一
  • 因为是按编号从小到大扫,所以第一次遇到这一最远距离的点,自然就是编号最小的答案

代码

cpp
#include <bits/stdc++.h>
using namespace std;

const int MAXN = 50000 + 5;
const int MAXM = 50000 * 2 + 5;
const int INF = 1e9;

int n, m;
int head[MAXN], to[MAXM], nxt[MAXM], edge_cnt;
int dist_arr[MAXN];

void init_graph() {
    edge_cnt = 0;
    for (int i = 1; i <= n; i++) {
        head[i] = 0;
        dist_arr[i] = INF;
    }
}

void add_edge(int u, int v) {
    edge_cnt++;
    to[edge_cnt] = v;
    nxt[edge_cnt] = head[u];
    head[u] = edge_cnt;
}

void bfs(int start) {
    queue<int> q;
    dist_arr[start] = 0;
    q.push(start);

    while (!q.empty()) {
        int u = q.front();
        q.pop();

        for (int i = head[u]; i != 0; i = nxt[i]) {
            int v = to[i];
            if (dist_arr[v] != INF) {
                continue;
            }

            dist_arr[v] = dist_arr[u] + 1;
            q.push(v);
        }
    }
}

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

    cin >> n >> m;
    init_graph();

    for (int i = 1; i <= m; i++) {
        int u, v;
        cin >> u >> v;
        add_edge(u, v);
        add_edge(v, u);
    }

    bfs(1);

    int best_id = 1;
    int best_dist = -1;
    int count = 0;

    for (int i = 1; i <= n; i++) {
        if (dist_arr[i] > best_dist) {
            best_dist = dist_arr[i];
            best_id = i;
            count = 1;
        }
        else if (dist_arr[i] == best_dist) {
            count++;
        }
    }

    cout << best_id << ' ' << best_dist << ' ' << count << '\n';

    return 0;
}

复杂度

一次 BFS:

  • O(N+M)O(N + M)

最后扫描所有点:

  • O(N)O(N)

总复杂度:

  • O(N+M)O(N + M)

空间复杂度:

  • O(N+M)O(N + M)

总结

这题最重要的是别被“最短路”三个字带偏。

它虽然属于最短路题,但本质上只是:

  • 无权图
  • 单源最短路
  • 找最远层

这种题最稳的选择就是 BFS。