GESP认证C++编程真题解析 | 202509 七级
欢迎大家订阅我的CSDN专栏:算法题解:C++与Python实现!
本专栏旨在帮助大家从基础到进阶 ,逐步提升编程能力,助力信息学竞赛备战!
专栏特色
1.经典算法练习:根据信息学竞赛大纲,精心挑选经典算法题目,提供清晰的代码实现与详细指导,帮助您夯实算法基础。
2.系统化学习路径:按照算法类别和难度分级,从基础到进阶,循序渐进,帮助您全面提升编程能力与算法思维。
适合人群:
- 准备参加蓝桥杯、GESP、CSP-J、CSP-S等信息学竞赛的学生
- 希望系统学习C++/Python编程的初学者
- 想要提升算法与编程能力的编程爱好者
附上汇总帖:GESP认证C++编程真题解析 | 汇总
编程题
P14077 连通图
【题目来源】
洛谷:[P14077 GESP202509 七级] 连通图 - 洛谷
【题目描述】
给定一张包含 \(n\) 个结点与 \(m\) 条边的无向图,结点依次以 \(1,2,…,n\) 编号,第 \(i\) 条边(\(1≤i≤m\))连接结点 \(u_i\) 与结点 \(v_i\)。如果从一个结点经过若干条边可以到达另一个结点,则称这两个结点是连通的。
你需要向图中加入若干条边,使得图中任意两个结点都是连通的。请你求出最少需要加入的边的条数。
注意给出的图中可能包含重边与自环。
【输入】
第一行,两个正整数 \(n,m\),表示图的点数与边数。
接下来 \(m\) 行,每行两个正整数 \(u_i,v_i\),表示图中一条连接结点 \(u_i\) 与结点 \(v_i\) 的边。
【输出】
输出一行,一个整数,表示使得图中任意两个结点连通所需加入的边的最少数量。
【输入样例】
4 4
1 2
2 3
3 1
1 4
【输出样例】
0
【算法标签】
《洛谷 P14077 连通图》 #图论# #并查集# #深度优先搜索DFS# #连通块# #GESP# #2025#
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 100005; // 定义最大节点数
int n; // 节点数量
int m; // 边数量
int ans = 0; // 存储连通分量数量
int p[N]; // 并查集父节点数组
bool vis[N]; // 标记数组,记录是否已统计过该连通分量
/**
* 并查集查找函数(带路径压缩)
* @param x 要查找的节点
* @return 节点x的根节点
*/
int find(int x)
{
if (p[x] != x) // 如果当前节点不是根节点
{
p[x] = find(p[x]); // 路径压缩
}
return p[x];
}
int main()
{
// 输入节点数和边数
cin >> n >> m;
// 初始化并查集,每个节点的父节点指向自己
for (int i = 1; i <= n; i++)
{
p[i] = i;
}
// 处理每条边,合并连通分量
while (m--)
{
int a, b;
cin >> a >> b;
// 合并a和b所在的连通分量
p[find(a)] = find(b);
}
// 统计连通分量数量
for (int i = 1; i <= n; i++)
{
int x = find(i); // 找到节点i的根节点
if (!vis[x]) // 如果该连通分量未被统计过
{
ans++; // 连通分量数量加1
vis[x] = 1; // 标记该连通分量已统计
}
}
/**
* 输出结果:
* 连通分量数量减1,即需要添加的边数
* 因为要将所有连通分量连接成一棵树需要ans-1条边
*/
cout << ans - 1 << endl;
return 0;
}
【运行结果】
4 4
1 2
2 3
3 1
1 4
0
P14078 金币收集
【题目来源】
洛谷:[P14078 GESP202509 七级] 金币收集 - 洛谷
【题目描述】
小 A 正在游玩收集金币的游戏。具体来说,在数轴上将会出现 \(n\) 枚金币,其中第 \(i\) 枚(\(1≤i≤n\))金币将会在时刻 \(t_i\) 出现在数轴上坐标为 \(x_i\) 的位置。小 A 必须在时刻 \(t_i\) 恰好位于坐标 \(x_i\),才可以获得第 \(i\) 枚金币。
游戏开始时为时刻 \(0\),此时小 A 的坐标为 \(0\)。正常来说,小 A 可以按游戏机的按键在数轴上左右移动,但不幸的是游戏机的左方向键失灵了。小 A 每个时刻只能选择保持不动,或是向右移动一个单位。换言之,如果小 A 在时刻 \(t\) 的坐标为 \(x\),那么他在时刻 \(t+1\) 的坐标只能是 \(x\) 或是 \(x+1\) 二者之一,分别对应保持不动和向右移动。
小 A 想知道他最多能收集多少枚金币。你能帮他收集最多的金币吗?
【输入】
第一行,一个正整数 \(n\),表示金币的数量。
接下来 \(n\) 行,每行两个正整数 \(x_i,t_i\),分别表示金币出现的坐标与时刻。
【输出】
输出一行,一个整数,表示小 A 最多能收集的金币数量。
【输入样例】
3
1 6
3 7
2 4
【输出样例】
2
【算法标签】
《洛谷 P14078 金币收集》 #线性DP# #GESP# #2025#
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 100005; // 定义最大数据量
int n; // 输入的数据对数
int maxn; // 未使用的变量(可删除)
int len; // 记录当前最长序列长度
// 定义节点结构体,存储x和t值
struct Node
{
int x, t;
} a[N], b[N]; // a数组存储原始数据,b数组存储最长序列
/**
* 比较函数,用于排序
* @param x 第一个节点
* @param y 第二个节点
* @return 先按x升序,x相同按t升序
*/
bool cmp(Node x, Node y)
{
if (x.x != y.x)
return x.x < y.x;
return x.t < y.t;
}
/**
* 二分查找函数,寻找插入位置
* @param x 要查找的节点
* @return 合适的插入位置
*/
int find(Node x)
{
int L = 1, R = len, mid;
while (L < R)
{
mid = (L + R) / 2;
if (x.x - b[mid].x > x.t - b[mid].t)
R = mid;
else
L = mid + 1;
}
return L;
}
int main()
{
cin >> n;
int cur = 0; // 记录有效数据数量
// 输入并筛选有效数据(x <= t)
for (int i = 1; i <= n; i++)
{
int x, t;
cin >> x >> t;
if (x <= t)
{
a[++cur].x = x;
a[cur].t = t;
}
}
// 对有效数据进行排序
sort(a + 1, a + cur + 1, cmp);
// 初始化最长序列
len = 1;
b[1] = a[1];
// 构建最长满足条件的序列
for (int i = 2; i <= cur; i++)
{
// 如果当前元素可以直接加入序列
if (a[i].x - b[len].x <= a[i].t - b[len].t)
{
b[++len] = a[i];
}
// 否则找到合适的位置替换
else
{
int j = find(a[i]);
b[j] = a[i];
}
}
// 输出最长序列长度
cout << len << endl;
return 0;
}
【运行结果】
3
1 6
3 7
2 4
2

浙公网安备 33010602011771号