GESP认证C++编程真题解析 | 202406 八级
欢迎大家订阅我的专栏:算法题解:C++与Python实现!
本专栏旨在帮助大家从基础到进阶 ,逐步提升编程能力,助力信息学竞赛备战!
专栏特色
1.经典算法练习:根据信息学竞赛大纲,精心挑选经典算法题目,提供清晰的代码实现与详细指导,帮助您夯实算法基础。
2.系统化学习路径:按照算法类别和难度分级,从基础到进阶,循序渐进,帮助您全面提升编程能力与算法思维。
适合人群:
- 准备参加蓝桥杯、GESP、CSP-J、CSP-S等信息学竞赛的学生
- 希望系统学习C++/Python编程的初学者
- 想要提升算法与编程能力的编程爱好者
附上汇总帖:GESP认证C++编程真题解析 | 汇总
编程题
P10725 最远点对
【题目来源】
洛谷:[P10725 GESP202406 八级] 最远点对 - 洛谷
【题目描述】
小杨有一棵包含 \(n\) 个节点的树,这棵树上的任意一个节点要么是白色,要么是黑色。
小杨想知道相距最远的一对不同颜色节点的距离是多少。
【输入】
第一行包含一个正整数 \(n\),代表树的节点数。
第二行包含 \(n\) 个非负整数 \(a_1,a_2,\dots,a_n\)(对于所有的 \(1\le i\le n\),均有 \(a_i\) 等于 0 或 1),其中如果 \(a_i=0\),则节点 \(i\) 的颜色为白色;如果 \(a_i=1\),则节点 \(i\) 的颜色为黑色。
之后 \(n-1\) 行,每行包含两个正整数 \(x_i,y_i\),代表存在一条连接节点 \(x_i\) 和 \(y_i\) 的边。
保证输入的树中存在不同颜色的点。
【输出】
输出一个整数,代表相距最远的一对不同颜色节点的距离。
【输入样例】
5
0 1 0 1 0
1 2
1 3
3 4
3 5
【输出样例】
3
【算法标签】
《洛谷 P10725 最远点对》 #树的遍历# #GESP# #2024#
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 100005, M = N * 2;
int n; // 节点数
int a[N]; // a[i]: 节点i的颜色,0或1
int col0[N], col1[N]; // col0[i]: 以节点i为根的子树中,距离i最近的0色节点的距离
// col1[i]: 以节点i为根的子树中,距离i最近的1色节点的距离
int ans; // 答案:最长异色路径长度
int h[N], e[M], ne[M], w[M], idx; // 邻接表存储树
// 添加无向边
void add(int a, int b, int c)
{
e[idx] = b; // 边的终点
w[idx] = c; // 边权,这里都是1
ne[idx] = h[a]; // 插入到链表头部
h[a] = idx; // 更新头指针
idx++; // 边编号自增
}
// 深度优先搜索
// u: 当前节点
// fa: 父节点
void dfs(int u, int fa)
{
// 初始化当前节点的col0和col1
if (a[u] == 1)
{
col1[u] = 0; // 如果当前节点是1色,距离为0
}
else
{
col0[u] = 0; // 如果当前节点是0色,距离为0
}
// 遍历所有邻居节点
for (int i = h[u]; i != -1; i = ne[i])
{
int j = e[i]; // 邻居节点
if (j == fa) // 如果是父节点,跳过
{
continue;
}
// 递归处理子树
dfs(j, u);
// 关键:更新答案
// 如果u是0色,j的子树中有1色节点,形成异色路径
if (col0[u] >= 0 && col1[j] >= 0)
{
ans = max(ans, col1[j] + col0[u] + 1);
}
// 如果u是1色,j的子树中有0色节点,形成异色路径
if (col1[u] >=0 && col0[j] >= 0)
{
ans = max(ans, col0[j] + col1[u] + 1);
}
// 更新当前节点的col0和col1
col1[u] = max(col1[u], col1[j] + 1);
col0[u] = max(col0[u], col0[j] + 1);
}
}
int main()
{
// 输入节点数
cin >> n;
// 初始化邻接表
memset(h, -1, sizeof(h));
// 输入每个节点的颜色
for (int i = 1; i <= n; i++)
{
cin >> a[i];
// 初始化col0和col1为负无穷
col0[i] = -1e9;
col1[i] = -1e9;
}
// 输入n-1条边,构建树
for (int i = 1; i < n; i++)
{
int u, v;
cin >> u >> v;
add(u, v, 1); // 添加无向边,边权为1
add(v, u, 1);
}
// 从节点1开始DFS
dfs(1, 0);
// 输出答案
cout << ans << endl;
return 0;
}
【运行结果】
5
0 1 0 1 0
1 2
1 3
3 4
3 5
3
P10726 空间跳跃
【题目来源】
洛谷:P10726 [GESP202406 八级] 空间跳跃 - 洛谷 (luogu.com.cn)
【题目描述】
小杨在二维空间中有 \(n\) 个水平挡板,并且挡板之间彼此不重叠,其中第 \(i\) 个挡板处于水平高度 \(h_i\),左右端点分别位于 \(l_i\) 与 \(r_i\)。
小杨可以在挡板上左右移动,当小杨移动到右端点时,如果再向右移动会竖直掉落,从而落到下方第一个挡板上, 移动到左端点时同理。小杨在挡板上每移动 \(1\) 个单位长度会耗费 \(1\) 个单位时间,掉落时每掉落 \(1\) 个单位高度也会耗费 \(1\) 个单位时间。
小杨想知道,从第 \(s\) 个挡板上的左端点出发到第 \(t\) 个挡板需要耗费的最少时间是多少?
注意:可能无法从第 \(s\) 个挡板到达到第 \(t\) 个挡板。
【输入】
第一行包含一个正整数 \(n\),代表挡板数量。
第二行包含两个正整数 \(s,t\),含义如题面所示。
之后 \(n\) 行,每行包含三个正整数 \(l_i,r_i,h_i\),代表第 \(i\) 个挡板的左右端点位置与高度。
【输出】
输出一个整数代表需要耗费的最少时间,如果无法到达则输出 \(-1\)。
【输入样例】
3
3 1
5 6 3
3 5 6
1 4 100000
【输出样例】
100001
【算法标签】
《洛谷 P10726 空间跳跃》 #动规规划DP# #最短路# #GESP# #2024#
【代码详解】
#include <bits/stdc++.h>
using namespace std;
struct Node
{
int id; // 平台编号
int l, r; // 平台左右端点
int h; // 平台高度
} a[1005]; // 平台数组
int n, s, t; // n: 平台数量, s: 起点平台编号, t: 终点平台编号
int dp[1005][2]; // dp[i][0]: 到达平台i左端点的最小时间
// dp[i][1]: 到达平台i右端点的最小时间
// 排序比较函数:按高度从高到低排序
bool cmp(Node x, Node y)
{
return x.h > y.h;
}
int main()
{
// 读入数据
cin >> n >> s >> t;
for (int i = 1; i <= n; i++)
{
cin >> a[i].l >> a[i].r >> a[i].h;
a[i].id = i; // 记录原始编号
}
// 检查合法性
if (a[s].h < a[t].h) // 起点高度必须不低于终点
{
cout << -1 << endl;
return 0;
}
else if (s == t) // 起点和终点相同
{
cout << 0 << endl;
return 0;
}
// 按高度从高到低排序
sort(a + 1, a + n + 1, cmp);
// 找到排序后起点和终点的位置
int k = 1;
while (a[k].id != s)
{
k++;
}
s = k; // 更新起点在排序后数组中的位置
k = 1;
while (a[k].id != t)
{
k++;
}
t = k; // 更新终点在排序后数组中的位置
// 初始化DP数组为无穷大
memset(dp, 0x3f, sizeof(dp));
// 初始化起点
dp[s][0] = 0; // 在起点平台左端点
dp[s][1] = a[s].r - a[s].l; // 在起点平台右端点,需要横向走过去
// 动态规划:从高到低处理平台
for (int i = s; i < t; i++) // i: 当前平台
{
// 情况1:从当前平台i的左端点下落
for (int j = i + 1; j <= t; j++) // j: 下方平台
{
// 检查是否能落到平台j上(当前平台左端点垂直下方在平台j范围内)
if (a[i].l >= a[j].l && a[i].l <= a[j].r)
{
if (j != t) // 如果不是终点平台
{
// 到达平台j左端点的最小时间
dp[j][0] = min(dp[j][0], dp[i][0] + (a[i].h - a[j].h) + (a[i].l - a[j].l));
// 到达平台j右端点的最小时间
dp[j][1] = min(dp[j][1], dp[i][0] + (a[i].h - a[j].h) + (a[j].r - a[i].l));
}
else // 如果是终点平台
{
// 只需要到达左端点(到达终点即可)
dp[j][0] = min(dp[j][0], dp[i][0] + (a[i].h - a[j].h));
}
break; // 只能落到第一个遇到的平台上
}
}
// 情况2:从当前平台i的右端点下落
for (int j = i + 1; j <= t; j++) // j: 下方平台
{
// 检查是否能落到平台j上(当前平台右端点垂直下方在平台j范围内)
if (a[i].r >= a[j].l && a[i].r <= a[j].r)
{
if (j != t) // 如果不是终点平台
{
// 到达平台j左端点的最小时间
dp[j][0] = min(dp[j][0], dp[i][1] + (a[i].h - a[j].h) + (a[i].r - a[j].l));
// 到达平台j右端点的最小时间
dp[j][1] = min(dp[j][1], dp[i][1] + (a[i].h - a[j].h) + (a[j].r - a[i].r));
}
else // 如果是终点平台
{
// 只需要到达左端点(到达终点即可)
dp[j][0] = min(dp[j][0], dp[i][1] + (a[i].h - a[j].h));
}
break; // 只能落到第一个遇到的平台上
}
}
}
// 输出结果
if (dp[t][0] == 0x3f3f3f3f) // 无法到达终点
{
cout << -1 << endl;
}
else
{
cout << min(dp[t][0], dp[t][1]) << endl; // 取到达终点左端点或右端点的最小值
}
return 0;
}
【运行结果】
3
3 1
5 6 3
3 5 6
1 4 100000
100001

浙公网安备 33010602011771号