GESP认证C++编程真题解析 | 202409 七级
欢迎大家订阅我的专栏:算法题解:C++与Python实现!
本专栏旨在帮助大家从基础到进阶 ,逐步提升编程能力,助力信息学竞赛备战!
专栏特色
1.经典算法练习:根据信息学竞赛大纲,精心挑选经典算法题目,提供清晰的代码实现与详细指导,帮助您夯实算法基础。
2.系统化学习路径:按照算法类别和难度分级,从基础到进阶,循序渐进,帮助您全面提升编程能力与算法思维。
适合人群:
- 准备参加蓝桥杯、GESP、CSP-J、CSP-S等信息学竞赛的学生
- 希望系统学习C++/Python编程的初学者
- 想要提升算法与编程能力的编程爱好者
附上汇总帖:GESP认证C++编程真题解析 | 汇总
编程题
P11248 矩阵移动
【题目来源】
洛谷:[P11248 GESP202409 七级] 矩阵移动 - 洛谷
【题目描述】
小杨有一个 \(n \times m\) 的矩阵,仅包含 01? 三种字符。矩阵的行从上到下编号依次为 \(1,2,\dots, n\),列从左到右编号依次为 \(1, 2, \dots, m\)。小杨开始在矩阵的左上角 \((1,1)\),小杨只能向下或者向右移动,最终到达右下角 \((n, m)\) 时停止,在移动的过程中每经过一个字符 1 得分会增加一分(包括起点和终点),经过其它字符则分数不变。小杨的初始分数为 \(0\) 分。
小杨可以将矩阵中不超过 \(x\) 个字符 ? 变为字符 1。小杨在修改矩阵后,会以最优的策略从左上角移动到右下角。他想知道自己最多能获得多少分。
【输入】
第一行包含一个正整数 \(t\),代表测试用例组数,接下来是 \(t\) 组测试用例。对于每组测试用例,一共 \(n + 1\) 行。
第一行包含三个正整数 \(n, m, x\),含义如题面所示。
之后 \(n\) 行,每行一个长度为 \(m\) 的仅含 01? 的字符串。
【输出】
对于每组测试用例,输出一行一个整数,代表最优策略下小杨的得分最多是多少。
【输入样例】
2
3 3 1
000
111
01?
3 3 1
000
?0?
01?
【输出样例】
4
2
【算法标签】
《洛谷 P11248 矩阵移动》 #动态规划DP# #GESP# #2024#
【代码详解】
#include <bits/stdc++.h>
using namespace std;
string m1[1000]; // 存储网格地图,m1[i]表示第i行字符串
int t, n, m, dp[505][505][300], k; // t: 测试用例数, n: 行数, m: 列数, k: 最大可使用'?'数
int main()
{
cin >> t; // 输入测试用例数
while (t--)
{
// 输入网格大小和k值
cin >> n >> m >> k;
// 读入网格,并在每行前添加一个字符,使索引从1开始
for (int i = 1; i <= n; i++)
{
cin >> m1[i];
m1[i] = "e" + m1[i]; // 在字符串前添加一个字符,使得列索引从1开始
}
// 初始化动态规划数组为0
memset(dp, 0, sizeof(dp));
// 动态规划计算最大路径
for (int i = 1; i <= n; i++) // 遍历行
{
for (int j = 1; j <= m; j++) // 遍历列
{
for (int b = 0; b <= k; b++) // 遍历使用的'?'数量
{
// 情况1:不使用当前位置的'?'(或当前位置是'1'或'0')
// 从上方或左方转移,不消耗'?'名额
dp[i][j][b] = (m1[i][j] == '1') + max(dp[i - 1][j][b], dp[i][j - 1][b]);
// 情况2:如果当前位置是'?',且还有'?'名额可用
if (b && m1[i][j] == '?')
{
// 将'?'当作'1',消耗一个'?'名额
dp[i][j][b] = max(dp[i][j][b], 1 + max(dp[i - 1][j][b - 1], dp[i][j - 1][b - 1]));
}
}
}
}
// 在所有可能的'?'使用数量中,找到最大价值
int ans = 0;
for (int i = 0; i <= k; i++)
{
ans = max(ans, dp[n][m][i]);
}
cout << ans << endl;
}
return 0;
}
【运行结果】
2
3 3 1
000
111
01?
4
3 3 1
000
?0?
01?
2
P11249 小杨寻宝
【题目来源】
洛谷:P11249 [GESP202409 七级] 小杨寻宝 - 洛谷
【题目描述】
小杨有一棵包含 \(n\) 个节点的树,树上的一些节点放置有宝物。
小杨可以任意选择一个节点作为起点并在树上移动,但是小杨只能经过每条边至多一次,当小杨经过一条边后,这条边就会消失。小杨每经过一个放置有宝物的节点就会取得该宝物。
小杨想请你帮他判断自己能否成功取得所有宝物。
【输入】
本题单个测试点内有多组测试数据。输入第一行包含一个正整数 \(t\),代表测试用例组数。
接下来是 \(t\) 组测试用例。对于每组测试用例,一共 \(n+1\) 行。
第一行包含一个正整数 \(n\),代表树的节点数。
第二行包含 \(n\) 个非负整数 \(a_1, a_2, \dots a_n\),其中如果 \(a_i = 1\),则节点 \(i\) 放置有宝物;若 \(a_i = 0\),则节点 \(i\) 没有宝物。
之后 \(n - 1\) 行,每行包含两个正整数 \(x_i, y_i\),代表存在一条连接节点 \(x_i\) 和 \(y_i\) 的边。
【输出】
对于每组测试数据,如果小杨能成功取得所有宝物,输出 Yes,否则输出 No。
【输入样例】
2
5
0 1 0 1 0
1 2
1 3
3 4
3 5
5
1 1 1 1 1
1 2
1 3
3 4
3 5
【输出样例】
Yes
No
【算法标签】
《洛谷 P11249 小杨寻宝》 #图论# #GESP# #2024#
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 100005, M = N * 2;
int T; // 测试用例数量
int n, a[N], root; // n: 节点数, a[i]: 节点i的初始值, root: 根节点
int h[N], e[M], ne[M], idx; // 邻接表存储树
bool flag; // 标志位,表示当前树是否合法
// 添加无向边
void add(int a, int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
// 深度优先搜索
// u: 当前节点
// fa: 父节点
void dfs(int u, int fa)
{
if (!flag) return; // 如果已经发现不合法,直接返回
int res = 0; // 记录子节点值的和
// 遍历所有子节点
for (int i = h[u]; i != -1; i = ne[i])
{
int j = e[i]; // 子节点
if (j == fa) // 跳过父节点
{
continue;
}
// 递归处理子节点
dfs(j, u);
// 累加子节点的值
res += a[j];
}
// 如果子节点的和不为0,将当前节点设为1
if (res)
{
a[u] = 1;
}
// 检查约束条件
if ((u == root && res > 2) || (u != root && res > 1))
{
flag = 0; // 不满足条件,标记为非法
}
}
int main()
{
cin >> T; // 读取测试用例数
while (T--)
{
cin >> n; // 读取节点数
// 初始化邻接表
memset(h, -1, sizeof(h));
idx = 0;
root = 0;
flag = 1; // 初始假设树是合法的
// 读取每个节点的初始值
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
// 找到值为1的节点作为根节点
for (int i = 1; i <= n; i++)
{
if (a[i] == 1)
{
root = i;
break;
}
}
// 读取树的边
for (int i = 1; i < n; i++)
{
int u, v;
cin >> u >> v;
add(u, v);
add(v, u);
}
// 从根节点开始DFS
dfs(root, 0);
// 输出结果
if (flag)
{
cout << "Yes" << endl;
}
else
{
cout << "No" << endl;
}
}
return 0;
}
【运行结果】
2
5
0 1 0 1 0
1 2
1 3
3 4
3 5
5
1 1 1 1 1
1 2
1 3
Yes
3 4
3 5
No

浙公网安备 33010602011771号