2024年6月GESP真题及题解(C++八级): 最远点对

2024年6月GESP真题及题解(C++八级): 最远点对

题目描述

小杨有一棵包含 n n n 个节点的树,这棵树上的任意一个节点要么是白色,要么是黑色。

小杨想知道相距最远的一对不同颜色节点的距离是多少。

输入格式

第一行包含一个正整数 n n n,代表树的节点数。

第二行包含 n n n 个非负整数 a 1 , a 2 , ⋯   , a n a_1,a_2,\cdots,a_n a1,a2,⋯,an(对于所有的 1 ≤ i ≤ n 1\le i\le n 1≤i≤n,均有 a i a_i ai 等于 0 0 0 或 1 1 1),其中如果 a i = 0 a_i=0 ai=0,则节点 i i i 的颜色为白色;如果 a i = 1 a_i=1 ai=1,则节点 i i i 的颜色为黑色。

之后 ( n − 1 ) (n-1) (n−1) 行,每行包含两个正整数 x i , y i x_i,y_i xi,yi,代表存在一条连接节点 x i x_i xi 和 y i y_i yi 的边。

保证输入的树中存在不同颜色的点。

输出格式

输出一个整数,代表相距最远的一对不同颜色节点的距离。

输入输出样例 1
输入 1
复制代码
5
0 1 0 1 0
1 2
1 3
3 4
3 5
输出 1
复制代码
3
说明/提示
样例解释

相距最远的不同颜色的一对节点为节点 2 2 2 和 5 5 5。

数据范围
子任务编号 得分 n n n a i a_i ai 特殊条件
1 1 1 30 30 30 ≤ 10 5 \le 10^5 ≤105 0 ≤ a i ≤ 1 0\le a_i\le 1 0≤ai≤1 树的形态为一条链
2 2 2 30 30 30 ≤ 10 3 \le 10^3 ≤103 0 ≤ a i ≤ 1 0\le a_i\le 1 0≤ai≤1
3 3 3 40 40 40 ≤ 10 5 \le 10^5 ≤105 0 ≤ a i ≤ 1 0\le a_i\le 1 0≤ai≤1

对于全部数据,保证有 1 ≤ n ≤ 10 5 1\le n\le 10^5 1≤n≤105, 0 ≤ a i ≤ 1 0\le a_i\le 1 0≤ai≤1。

思路分析

  1. 理解问题本质:在树上找到距离最远的两个异色节点,距离定义为节点间路径的边数。

  2. 暴力解法不可行:直接枚举所有异色节点对是 O(n²) 的,n ≤ 10⁵ 时不可行。

  3. 高效解法思路

    • 树的直径通常可以通过两次 BFS/DFS 找到最远点对
    • 但这里要求是不同颜色的节点对
    • 关键观察:最远异色节点对一定包含某个颜色的最远点之一
  4. 算法设计

    • 对于每种颜色,找到距离该颜色所有节点最远的节点
    • 从白色节点中找到距离所有黑色节点最远的点
    • 从黑色节点中找到距离所有白色节点最远的点
    • 这两个距离中的最大值就是答案
  5. 实现方法

    • 进行三次 BFS:
      1. 从任意节点开始,找到最远的节点 A
      2. 从 A 开始,记录到所有节点的距离 distA
      3. 从 B(与 A 不同颜色)开始,记录到所有节点的距离 distB
    • 答案 = max(从白色到黑色的最远距离, 从黑色到白色的最远距离)

代码实现

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

const int N = 1e5 + 5;  // 最大节点数+5的缓冲区

int n;                 // 树的节点数
int c[N];              // 颜色数组,c[i]表示节点i的颜色:0-白色,1-黑色
vector<int> g[N];      // 邻接表,存储树的结构
int d[2][N];           // 距离数组,d[0][i]表示从A到i的距离,d[1][i]表示从B到i的距离

// BFS函数:从起点s开始进行广度优先搜索,计算到所有节点的距离
// 参数:s - 起点,dist - 存储距离的数组指针
// 返回值:BFS过程中最后访问的节点(即距离起点最远的节点)
int bfs(int s, int* dist) {
    queue<int> q;  // BFS队列
    
    // 初始化距离数组,将所有距离设为-1表示未访问
    fill(dist + 1, dist + n + 1, -1);
    
    // 起点距离为0,加入队列
    dist[s] = 0;
    q.push(s);
    int last = s;  // 记录最后访问的节点
    
    // BFS主循环
    while (!q.empty()) {
        int u = q.front(); q.pop();  // 取出队首节点
        last = u;  // 更新最后访问的节点
        
        // 遍历u的所有邻居
        for (int v : g[u]) {
            // 如果邻居v尚未访问过
            if (dist[v] == -1) {
                dist[v] = dist[u] + 1;  // 更新距离
                q.push(v);              // 加入队列
            }
        }
    }
    
    return last;  // 返回最后访问的节点(即最远节点)
}

int main() {
    // 输入输出优化
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    // 读入节点数
    cin >> n;
    
    // 读入每个节点的颜色
    for (int i = 1; i <= n; i++) cin >> c[i];
    
    // 读入树的边,构建邻接表
    for (int i = 1; i < n; i++) {
        int u, v;
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    
    // 第一步:第一次BFS,从任意节点(节点1)出发,找到最远点A
    int A = bfs(1, d[0]);
    
    // 第二步:第二次BFS,从A出发,找到最远点B
    // 同时,d[0]数组现在存储的是从A到所有节点的距离
    int B = bfs(A, d[0]);
    
    // 第三步:第三次BFS,从B出发
    // d[1]数组将存储从B到所有节点的距离
    bfs(B, d[1]);
    
    // 现在我们有:
    // d[0][i] - 从A到节点i的距离
    // d[1][i] - 从B到节点i的距离
    
    int ans = 0;  // 存储最终答案(最远异色节点对的距离)
    
    // 情况1:考虑以A为端点的最远异色节点对
    // 遍历所有节点,找到与A颜色不同的节点,更新最大距离
    for (int i = 1; i <= n; i++) {
        if (c[i] != c[A]) {           // 如果节点i与A颜色不同
            ans = max(ans, d[0][i]);  // 更新答案,取当前最大值
        }
    }
    
    // 情况2:考虑以B为端点的最远异色节点对
    // 遍历所有节点,找到与B颜色不同的节点,更新最大距离
    for (int i = 1; i <= n; i++) {
        if (c[i] != c[B]) {           // 如果节点i与B颜色不同
            ans = max(ans, d[1][i]);  // 更新答案,取当前最大值
        }
    }
    
    // 输出最终答案
    cout << ans << "\n";
    
    return 0;
}

功能分析

算法思路
  1. 核心思想:利用树的直径和 BFS 性质
  2. 关键观察:最远异色点对一定包含从某个点出发能到达的最远异色点
  3. 正确性保证:对于任意节点,其最远点一定是直径端点之一
时间复杂度
  • 三次 BFS:O(n)
  • 两次遍历所有节点:O(n)
  • 总复杂度:O(n),满足 n ≤ 10⁵ 的要求
空间复杂度
  • 邻接表:O(n)
  • 距离数组:O(n)
  • 总空间:O(n)
注意事项
  1. 使用 ios::sync_with_stdio(false)cin.tie(0) 加速输入
  2. BFS 使用队列实现,避免递归栈溢出
  3. 颜色数组下标从 1 开始,符合题目输入
  4. 保证至少有一对异色节点(题目保证)
测试用例验证

对于样例:

复制代码
5
0 1 0 1 0
1 2
1 3
3 4
3 5

树结构:

复制代码
    1
   / \
  2   3
     / \
    4   5

颜色:1(0), 2(1), 3(0), 4(1), 5(0)

最远异色点对:节点 2(黑) 和 节点 5(白)

距离 = 2->1->3->5 = 3

算法输出:3


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

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;
}
相关推荐
小龙报2 小时前
【C语言进阶数据结构与算法】LeetCode27 && LeetCode88顺序表练习:1.移除元素 2.合并两个有序数组
c语言·开发语言·数据结构·c++·算法·链表·visual studio
无限进步_2 小时前
C语言实现贪吃蛇游戏完整教程【最终版】
c语言·开发语言·c++·git·游戏·github·visual studio
被星1砸昏头2 小时前
自定义操作符高级用法
开发语言·c++·算法
嘿BRE2 小时前
【C++】智能指针
c++
Tisfy2 小时前
LeetCode 3315.构造最小位运算数组 II:位运算
算法·leetcode·题解·位运算
楼田莉子2 小时前
Linux学习之库的原理与制作
linux·运维·服务器·c++·学习
浅念-3 小时前
C++第一课
开发语言·c++·经验分享·笔记·学习·算法
charlie1145141913 小时前
现代嵌入式C++教程:对象池(Object Pool)模式
开发语言·c++·学习·算法·嵌入式·现代c++·工程实践
HABuo3 小时前
【linux进程控制(三)】进程程序替换&自己实现一个bash解释器
linux·服务器·c语言·c++·ubuntu·centos·bash