题解:洛谷 P2959 The Leisurely Stroll

【题目来源】

洛谷:P2959 [USACO09OCT] The Leisurely Stroll G - 洛谷

【题目描述】

Bessie 通过牛棚的大门向外望去,发现今天是一个美丽的春季早晨。她想,“我真的好想好想沐浴着春风,走在草地之中,感受嫩草温柔地抚摸四蹄的感觉。”她知道一旦她离开了牛棚,她将沿着一条小径走一段路,然后就会出现一个三岔路口,她必须在两条小径中选择一条继续走下去。然后她又会遇到更多的三岔路口,进行更多的选择,直到她到达一个青翠的牧场为止。

她决定作一个选择使得她在去吃早草的路途中可以走过最多的小径。给你这些小径的描述,求出 Bessie 最多可以走过多少条小径。假定 Bessie 一出牛棚就有 \(2\) 条路径,Bessie 需要从中选择一条。

农场中有 \(P-1\)\(1 \le P \le 1000\))个分岔节点(范围是 \(1 \ldots P-1\)),引向 \(P\) 片草地,它们之间由小径连接。对任意一个节点来说,只有一条从牛棚(被标记为节点 \(1\))开始的路径可以到达。

考虑下面的图。线段表示小径,% 表示草地。右边的图中的 # 表示一条到达草地的高亮的路径。


                 %                             %
                /                             /
      2----%   7----8----%          2----%   7####8----%
     / \      /      \             # #      #      #
    1   5----6        9----%      1   5####6        9----%
     \   \    \        \           \   \    \        #
      \   %    %        %           \   %    %        %
       \                             \
        3-----%                       3-----%
         \                             \
          4----%                        4----%
           \                             \
            %                             %

从分岔节点 \(9\) 到达的草地是两个可以让 Bessie 走过最多小径的草地之一。在去吃早草的路上 Bessie 将走过 \(7\) 条不同的小径。这些草地是离牛棚也就是节点 \(1\) 最“远”的。

\(3\) 个整数来表示每一个节点:\(C,D_1,D_2\)\(C\) 是节点的编号(\(1 \le C < P\)),\(D_1\)\(D_2\) 是由该节点引出的两条小径的终点(\(0 \le D_1,D_2 < P\))。如果 \(D_1\)\(0\),表示这条小径引向的是一片牧草地,\(D_2\) 也一样。

【输入】

第一行,一个正整数 \(P\)

以下 \(P-1\) 行,每行 \(3\) 个整数 \(C,D_1,D_2\) 表示一个节点。

【输出】

输出一行一个整数,代表 Bessie 可以经过最多节点数。

【输入样例】

10
7 8 0
5 0 6
9 0 0
6 0 7
3 4 0
2 5 0
8 0 9
4 0 0
1 2 3

【输出样例】

7

【核心思想】

  1. 问题分析:给定一棵以节点 \(1\) 为根的满二叉树(每个非叶子节点恰好有两条出边,分别指向子节点或叶子草地),求从根节点到最远叶子节点的路径长度(经过的边数)。这是一个树的最长路径/BFS求树高问题,关键在于将节点信息按编号排序后,用BFS从根节点开始逐层扩展,记录最大深度。

  2. 算法选择

    • 按节点编号排序:输入的节点信息无序,先按 \(C\) 排序确保节点按编号顺序处理
    • BFS层序扩展:从根节点的两个子节点开始BFS,逐层向下遍历,记录到达每个节点的深度(经过的边数)
    • 叶子节点判定\(D_1 = 0\)\(D_2 = 0\) 表示该方向指向草地(叶子),更新最大深度
  3. 关键步骤

    • 读取输入\(P\)\(P-1\) 个节点的 \((C, D_1, D_2)\)
    • 初始化:所有节点的 \(step = 0\)
    • 排序:按 \(C\) 从小到大排序节点
    • BFS初始化:将节点 \(1\) 的两个子节点 \(D_1\)\(D_2\) 入队,\(step = 1\)
    • BFS扩展
      • 取出队首 \(tp\)
      • 检查 \(D_1\)
        • 若为 \(0\)(叶子):\(maxn = \max(maxn, p[tp].step)\)
        • 否则:子节点入队,\(step = p[tp].step + 1\)
      • 检查 \(D_2\):同上
    • 输出 \(maxn + 1\)(加上从牛棚到节点 \(1\) 的那条边)
  4. 时间/空间复杂度

    • 时间复杂度:\(O(P \log P)\),排序主导
    • 空间复杂度:\(O(P)\),节点数组和队列
  5. BFS求树高的核心思想

    • 树的层次遍历:BFS天然按层扩展,每扩展一层深度加 \(1\),首次到达叶子节点时的深度即为该路径长度
    • 排序恢复结构:输入无序,按节点编号排序后用数组索引直接访问子节点,避免复杂建树
    • 满二叉树特性:每个节点恰好有两个分支(子节点或叶子),BFS时统一处理即可
    • 适用于"无序输入的树结构 + 求最远叶子距离"类问题,核心是排序后用BFS层序遍历

【解题思路】

【算法标签】

普及- #BFS-一维

【代码详解】

#include <bits/stdc++.h>
using namespace std;
int P, maxn, maxc;
struct node {
    int c, d1, d2, step;
}p[1005];
bool cmp(node x, node y)
{
    return x.c < y.c;
}
int main()
{
    cin >> P;  // 输入P
    for (int i=1; i<P; i++) {  // 遍历P-1次的输入
        cin >> p[i].c >> p[i].d1 >> p[i].d2; // 记录每个数c及对应的d1、d2
        p[i].step=0;  // 初始化到达每个牛棚的step为0
    }
    sort(p+1, p+P, cmp);  // 按照从小到大排序
    queue<int> q;  // 定义队列q
    int tp = {p[1].d1};  // 将牛棚1的d1和d2压入队列中
    p[p[1].d1].step = p[1].step+1;  // 同时更新其step为原来牛棚1的step加1
    q.push(tp);
    tp = {p[1].d2};
    p[p[1].d2].step = p[1].step+1;  // 同时更新其step为原来牛棚1的step加1
    q.push(tp);
    while (!q.empty()) {  // BFS模板
        tp = q.front();
        q.pop();
        if (p[tp].d1==0) {  // 当前牛棚的d1为0时
            if (p[tp].step>maxn) {  // 判断其step是否比maxn还大
                maxn = p[tp].step;  // 更新maxn
            }
        } else {  // 否则将p[tp].d1压入队列
            int t = p[tp].d1;
            q.push(t);
            p[t].step = p[tp].step+1;  // 更新其step为当前牛棚的step加1
        }
        if (p[tp].d2==0) {  // 当前牛棚的d2为0时
            if (p[tp].step>maxn) {  // 判断其step是否比maxn还大
                maxn = p[tp].step;  // 更新maxn
            }
        } else {  // 否则将p[tp].d2压入队列
            int t = p[tp].d2;
            q.push(t);
            p[t].step = p[tp].step+1;  // 更新其step为当前牛棚的step加1
        }
    }
    cout << maxn+1 << endl;  // 最后输出maxn,需要再加上1(从牛棚到草坪的那段路径)
    return 0;
}

【运行结果】

10
7 8 0
5 0 6
9 0 0
6 0 7
3 4 0
2 5 0
8 0 9
4 0 0
1 2 3
7
posted @ 2026-08-28 15:56  团爸讲算法  阅读(4)  评论(0)    收藏  举报