学而思编程每周练习赛 | 2026年春第1周
附上学而思公众号链接:学而思编程每周练习赛-第01周-习题视频讲解与试题分析
语言基础组
演讲比赛
【题目来源】
学而思编程:演讲比赛
【题目描述】
小X的学校组织了一场演讲比赛,由 \(n\) 位评委对参赛选手进行打分。打分规则是去掉 \(n\) 位评委中最高得分和最低得分后,计算出剩余 \(n−2\) 位评委分数的平均值(保留两位小数)作为最后得分。
小X同学也积极参加了本次演讲比赛,请你帮小X计算一下他的最后得分。
【输入】
输入有 \(2\) 行,第 \(1\) 行,一个正整数 \(n\),表示有 \(n\) 位评委。
第 \(2\) 行,有 \(n\) 个正整数 \(p_i\),表示每一位评委的打分。
【输出】
输出一个数字,表示小X的最后得分,保留两位小数。
【输入样例】
8
50 90 55 78 52 68 66 93
【输出样例】
68.17
【解题思路】

【代码详解】
#include <bits/stdc++.h>
using namespace std;
int main()
{
// 输入数据个数n
int n;
cin >> n;
// 初始化最大值、最小值和总和
int mx = 0; // 存储最大值,初始设为最小值
int mn = 100; // 存储最小值,初始设为最大值
int sum = 0; // 存储所有数的总和
// 循环读取n个数
for (int i = 1; i <= n; i++)
{
int p;
cin >> p; // 读取当前数
// 累加到总和
sum += p;
// 更新最大值
if (p > mx)
{
mx = p;
}
// 更新最小值
if (p < mn)
{
mn = p;
}
}
// 计算去掉一个最高分和一个最低分后的平均分
// 注意:n-2是除数,因为去掉了两个数
double avg = 1.0 * (sum - mx - mn) / (n - 2);
// 输出结果,保留两位小数
printf("%.2lf\n", avg);
return 0;
}
【运行结果】
8
50 90 55 78 52 68 66 93
68.17
平均数
【题目来源】
学而思编程:平均数
【题目描述】
读取 \(4\) 个数字 \(N1,N2,N3,N4\),这 \(4\) 个数字都是保留 \(1\) 位小数的浮点数,对应于学生获得的 \(4\) 个分数。
这 \(4\) 个分数的权重分别为 \(2,3,4,1\),请你计算学生成绩的平均值 \(X\) 并输出 Media: X。其中,学生平均成绩\(=(2∗N1+3∗N2+4∗N3+N4)/10\)。
接下来分为以下三种情况:
如果平均值为 \(7.0\) 或更高,则输出 Aluno aprovado.。
如果平均值小于 \(5.0\),则输出 Aluno reprovado.。
如果平均值大于等于 \(5.0\) 并且小于 \(7.0\),则输出 Aluno em exame.,并再读取一个数字 \(Y\),然后输出 Nota do exame: Y。接下来重新计算平均值 \(Z=(X+Y)/2\),如果 \(Z\) 大于或等于 \(5.0\),则输出 Aluno aprovado.,否则输出 Aluno reprovado.。最后输出 Media final: Z,表示学生的最终成绩。
【输入】
输入中包含四个浮点数,表示学生的四个成绩。
也有部分满足情况 \(3\) 的数据,多包含一个浮点数
【输出】
输出的结果均保留 \(1\) 位小数,具体形式参照题目描述和输出样例。
【输入样例】
2.0 4.0 7.5 8.0
6.4
【输出样例】
Media: 5.4
Aluno em exame.
Nota do exame: 6.4
Aluno aprovado.
Media final: 5.9
【解题思路】

【代码详解】
#include <bits/stdc++.h>
using namespace std;
int main()
{
// 定义变量并输入四个成绩
double a, b, c, d;
cin >> a >> b >> c >> d;
// 计算加权平均成绩(权重分别为2,3,4,1)
double x = (2 * a + 3 * b + 4 * c + d) / 10;
// 输出平均成绩,保留1位小数
printf("Media: %.1lf\n", x);
// 判断学生成绩状态
if (x >= 7.0)
{
// 平均分≥7.0,直接通过
cout << "Aluno aprovado." << endl;
}
else if (x < 5.0)
{
// 平均分<5.0,直接不通过
cout << "Aluno reprovado." << endl;
}
else
{
// 5.0≤平均分<7.0,需要补考
cout << "Aluno em exame." << endl;
// 输入补考成绩
double y;
cin >> y;
printf("Nota do exame: %.1lf\n", y);
// 计算最终成绩(原成绩和补考成绩的平均)
double z = (x + y) / 2;
// 判断补考后是否通过
if (z >= 5.0)
{
cout << "Aluno aprovado." << endl;
}
else
{
cout << "Aluno reprovado." << endl;
}
// 输出最终成绩
printf("Media final: %.1lf\n", z);
}
return 0;
}
【运行结果】
2.0 4.0 7.5 8.0
Media: 5.4
Aluno em exame.
6.4
Nota do exame: 6.4
Aluno aprovado.
Media final: 5.9
蚂蚁
【题目来源】
学而思编程:蚂蚁
【题目描述】
有 \(n\) 蚂蚁在长度为 \(L\) 单位的木棍上,每只蚂蚁都有一个初始的位置和初始朝向(任意两只蚂蚁的初始位置不同),每只蚂蚁都以每秒一个单位的速度向前移动。其中一部分蚂蚁向左移动,其他蚂蚁向右移动。
当两只向不同方向移动的蚂蚁在某个点相遇时,它们会同时改变移动方向并继续移动。假设更改方向不会花费任何额外时间。
而当蚂蚁在某一时刻 \(t\) 到达木板的一端时,它立即从木板上掉下来,初始时刻 \(t=0\)。请你用编程实现:最后一只蚂蚁从木棍上掉下来的时刻。
【输入】
输入有 \(n+1\) 行,第 \(1\) 行两个正整数 \(n,L\);
第 \(2\) 行到第 \(n+1\) 行,每行两个整数 \(x_i,d_i\),\(x_i\) 表示第 \(i\) 只蚂蚁的初始位置,\(d_i\) 表示第 \(i\) 只蚂蚁的初始朝向,其中 \(d_i=0\) 表示第 \(i\) 只蚂蚁初始朝向向左,\(d_i=1\) 表示第 \(i\) 只蚂蚁初始朝向向右。
【输出】
输出一个整数 \(t\),最后一只蚂蚁从木棍上掉下来的时刻。
【输入样例】
4 4
0 1
1 1
3 0
4 0
【输出样例】
4
【解题思路】

【代码详解】
#include <bits/stdc++.h>
using namespace std;
int main()
{
// 输入n(物品数量)和L(总长度)
int n, L;
cin >> n >> L;
// 初始化最大距离ans为0
int ans = 0;
// 遍历每个物品
for (int i = 1; i <= n; i++)
{
// 输入物品的位置x和方向d
int x, d;
cin >> x >> d;
// 根据物品方向处理
if (d == 0)
{
// 方向为0(向左):最大距离就是x本身
if (x > ans)
{
ans = x;
}
}
else
{
// 方向为1(向右):最大距离是L-x
if (L - x > ans)
{
ans = L - x;
}
}
}
// 输出所有物品中的最大距离
cout << ans;
return 0;
}
【运行结果】
4 4
0 1
1 1
3 0
4 0
4
普及奠基组
三角形牧场
【题目来源】
学而思编程:三角形牧场
【题目描述】
Farmer John 想要给他的奶牛们建造一个三角形牧场。
有 \(N\) 个栅栏柱子分别位于农场的二维平面上不同的点 \((X_1,Y_1)\dots(X_N,Y_N)\)。
他可以选择其中三个点组成三角形牧场,只要三角形有一条边与 \(x\) 轴平行,且有另一条边与 \(y\) 轴平行。
FJ 可以组成的合法三角形的最大面积多少 ?保证存在至少一个合法的三角形牧场。
【输入】
第一行包含 \(N\)。
以下 \(N\) 行每行包含两个整数 \(X_i\) 和 \(Y_i\),均在范围 \(-10^4\dots 10^4\) 之内,描述一个栅栏柱子的位置。
【输出】
由于面积不一定为整数,输出栅栏柱子可以围成的合法三角形的最大面积的两倍。
【输入样例】
4
0 0
0 1
1 0
1 2
【输出样例】
2
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int x[105], y[105]; // 存储n个点的x坐标和y坐标
int main()
{
int n;
cin >> n; // 读入点的数量
// 读入n个点的坐标
for (int i = 1; i <= n; i++)
{
cin >> x[i] >> y[i];
}
int ans = 0; // 初始化最大矩形面积为0
// 三重循环枚举矩形的三个顶点
for (int i = 1; i <= n; i++) // 第一个点:矩形的左下角
{
for (int j = 1; j <= n; j++) // 第二个点:与第一个点同x轴
{
for (int k = 1; k <= n; k++) // 第三个点:与第一个点同y轴
{
// 检查是否能构成与坐标轴平行的矩形
// 条件1: 点i和点k有相同的x坐标(垂直线段)
// 条件2: 点j和点i有相同的y坐标(水平线段)
if (x[i] == x[k] && y[j] == y[i])
{
// 计算矩形面积 = 宽度 * 高度
// 宽度 = |x[i] - x[j]| (点i和点j的x坐标差)
// 高度 = |y[i] - y[k]| (点i和点k的y坐标差)
int area = abs(y[i] - y[k]) * abs(x[i] - x[j]);
ans = max(ans, area); // 更新最大面积
}
}
}
}
cout << ans << endl; // 输出最大矩形面积
return 0;
}
【运行结果】
4
0 0
0 1
1 0
1 2
2
疯狂的科学家
【题目来源】
学而思编程:疯狂的科学家
【题目描述】
Farmer John 的远房亲戚 Ben 是一个疯狂的科学家。通常这会在家庭聚会时造成不小的摩擦,但这偶尔也会带来些好处,尤其是当 Farmer John 发现他正面对一些有关他的奶牛们的独特而不寻常的问题时。
Farmer John 当前正面对一个有关她的奶牛们的独特而不寻常的问题。他最近订购了 \(N\) 头奶牛(\(1≤N≤1000\)),包含两种不同品种:荷斯坦牛和更赛牛。他在订单中用一个长为 \(N\) 的字符串来指定奶牛,其中的字符为 H(表示荷斯坦牛)或 G(表示更赛牛)。不幸的是,当这些奶牛到达他的农场,他给她们排队时,她们的品种组成的字符串与原先的不同。
我们将这两个字符串称为 \(A\) 和 \(B\),其中 \(A\) 是 Farmer John 原先想要的品种字符组成的字符串,\(B\) 是他的奶牛们到达时组成的字符串。Farmer John 并没有简单地检查重新排列 \(B\) 中的奶牛是否能得到 \(A\),而是请他的远房亲戚 Ben 利用他的科学才华来解决这一问题。
经过数月的研究,Ben 发明了一台不同寻常的机器:奶牛品种转换机,能够选择任意奶牛组成的子串并反转她们的品种:在这个子串中的所有 H 变为 G,所有 G 变为 H。Farmer John 想要求出将他当前的序列 \(B\) 变为他本来订购时想要的 \(A\) 需要使用这台机器的最小次数。然而,Ben 的疯狂的科学家技能并不会处理开发奇异机器以外的事,所以你需要帮助 Farmer John 解决这个计算难题。
【输入】
输入的第一行包含 \(N\),以下两行包含字符串 \(A\) 和 \(B\)。每个字符串均包含 \(N\) 个字符,字符均为 H 和 G 之一。
【输出】
输出将 \(B\) 变为 \(A\) 需要使用机器的最小次数。
【输入样例】
7
GHHHGHH
HHGGGHH
【输出样例】
2
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n; // 字符串长度
string a, b; // 两个字符串
cin >> n >> a >> b; // 读入长度和两个字符串
int cnt = 0, flag = 1; // cnt: 不同段数量,flag: 标记是否在新的一段中
for (int i = 0; i < n; i++) // 遍历每个位置
{
// 如果当前位置字符不同,且flag为1(表示新的一段开始)
if (a[i] != b[i] && flag)
{
cnt++; // 不同段数量加1
flag = 0; // 标记进入当前不同段
}
// 如果当前位置字符相同
if (a[i] == b[i])
{
flag = 1; // 标记可以开始新的不同段
}
}
cout << cnt << endl; // 输出不同段的数量
return 0;
}
【运行结果】
7
GHHHGHH
HHGGGHH
2
更奇怪的照片
【题目来源】
学而思编程:更奇怪的照片
【题目描述】
Farmer John 正再一次尝试给他的 \(N\) 头奶牛拍照(\(2≤N≤1000\))。
每头奶牛有一个范围在 \(1…100\) 之内的整数的「品种编号」。Farmer John 对他的照片有一个十分古怪的构思:他希望将所有的奶牛分为不相交的若干组(换句话说,将每头奶牛分到恰好一组中)并将这些组排成一行,使得第一组的奶牛的品种编号之和为偶数,第二组的编号之和为奇数,以此类推,奇偶交替。
Farmer John 可以分成的最大组数是多少?
【输入】
输入的第一行包含 \(N\)。下一行包含 \(N\) 个空格分隔的整数,为 \(N\) 头奶牛的品种编号。
【输出】
输出 Farmer John 的照片中的最大组数。可以证明,至少存在一种符合要求的分组方案。
【输入样例】
7
1 3 5 7 9 11 13
【输出样例】
3
【代码详解】
#include <bits/stdc++.h>
using namespace std;
int n, odd, even; // n: 数字个数,odd: 奇数个数,even: 偶数个数
int main()
{
cin >> n; // 读入数字个数
for (int i = 1; i <= n; i++) // 遍历每个数字
{
int t;
cin >> t; // 读入当前数字
if (t % 2 == 0) // 如果是偶数
{
even++; // 偶数计数加1
}
else // 如果是奇数
{
odd++; // 奇数计数加1
}
}
// 调整奇偶数量,使它们可以交替排列
while (odd > even) // 当奇数多于偶数时
{
odd -= 2; // 将两个奇数合并成一个偶数
even++; // 合并后得到一个偶数
}
if (even > odd) // 如果偶数多于奇数
{
even = odd + 1; // 最多只能多一个偶数
}
// 输出最大交替序列长度
cout << even + odd << endl; // 总长度是奇数和偶数个数之和
return 0;
}
【运行结果】
7
1 3 5 7 9 11 13
3
普及进阶组
重新涂色
【题目来源】
学而思编程:重新涂色
【题目描述】
有一个 \(H\) 行 \(W\) 列的方格棋盘, 从上数第 \(i\) 行,从左数第 \(j\) 列的格子记为 \((i,j)\). 小猴从 \((1,1)\) 出发, 每次可以走到上下左右的相邻格子, 目标是走到 \((H,W)\). 方格被涂成了黑色和白色, 小猴只能进入白色的格子, 不能进入黑色格子.
在游戏开始前, 小猴可以将任意多个白色格子涂黑, 但是不能涂黑起点 \((1,1)\) 和终点 \((H,W)\). 涂色必须在游戏开始前完成, 开始游戏后就不能涂黑格子了.
到达终点后, 小猴能得到的分数等于游戏开始前涂黑的格子的数量, 给出初始的棋盘状态, 求出小猴能得到的最高分数. 如果无法到达终点, 输出 \(-1\).
【输入】
第一行, \(2\) 个正整数 \(H,W\), 表示棋盘行数和列数
接下来 \(H\) 行, 每行 \(W\) 个字符, 给出棋盘初始状态, '#'表示黑格, '.'表示白格
【输出】
输出小猴能得到的最高分数, 若无法到达终点, 输出 \(-1\).
【输入样例】
3 3
..#
#..
...
【输出样例】
2
【解题思路】

【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 55;
int n, m, cnt;
char g[N][N]; // 地图
int dist[N][N]; // 存储从起点到每个位置的最短距离
int dx[4] = {-1, 0, 1, 0};
int dy[4] = {0, 1, 0, -1};
struct Node
{
int x, y;
};
queue<Node> q;
// 广度优先搜索,计算从起点(1,1)到所有可到达位置的最短距离
void bfs()
{
q.push((Node){1, 1});
memset(dist, -1, sizeof(dist));
dist[1][1] = 0;
while (!q.empty())
{
int x = q.front().x, y = q.front().y;
q.pop();
for (int i = 0; i < 4; i++)
{
int nx = x + dx[i], ny = y + dy[i];
// 检查边界
if (nx < 1 || nx > n || ny < 1 || ny > m)
{
continue;
}
// 检查是否是障碍物
if (g[nx][ny] == '#')
{
continue;
}
// 检查是否已访问
if (dist[nx][ny] != -1)
{
continue;
}
dist[nx][ny] = dist[x][y] + 1;
q.push((Node){nx, ny});
}
}
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
cin >> g[i][j];
// 统计空地数量
if (g[i][j] == '.')
{
cnt++;
}
}
}
bfs();
// 如果无法到达终点
if (dist[n][m] == -1)
{
cout << -1 << endl;
}
else
{
// 输出结果:总空地数 - 最短路径经过的格子数
// dist[n][m]是从起点到终点的步数,+1是因为包括起点
cout << cnt - (dist[n][m] + 1) << endl;
}
return 0;
}
【运行结果】
3 3
..#
#..
...
2
六面世界3
【题目来源】
【题目描述】
如下所示,有一个无限扩展的六边形网格。最初,所有格子都是白色的。

一个六边形格子用两个整数 \(i,j\) 表示为 \((i,j)\)。
格子 \((i,j)\) 与以下 \(6\) 个格子相邻:
\((i−1,j−1)\)
\((i−1,j)\)
\((i,j−1)\)
\((i,j+1)\)
\((i+1,j)\)
\((i+1,j+1)\)
现在有 \(N\) 个格子 \((X_1,Y_1),(X_2,Y_2),…,(X_N,Y_N)\) 被涂成了黑色。
如果两个黑色格子之间可以通过若干个相邻的黑色格子移动到达,则认为这两个黑色格子属于同一个连通分量。请你求出黑色格子构成的连通分量的个数。
【输入】
第一行,一个正整数 \(N\)
接下来 \(N\) 行,每行两个整数 \((X_i,Y_i)\),表示黑色格子的坐标。
【输出】
输出黑色格子构成的连通分量的个数。
【输入样例】
6
-1 -1
0 1
0 2
1 0
1 2
2 0
【输出样例】
3
【代码详解】
#include <bits/stdc++.h>
using namespace std;
const int N = 2005;
int a[N][N]; // 标记哪些位置有点
int n, cnt;
bool vis[N][N]; // 标记是否访问过
// 六个方向的偏移量,对应六边形的六个方向
int dx[] = {1, -1, 0, 0, 1, -1};
int dy[] = {0, 0, 1, -1, 1, -1};
// 深度优先搜索
void dfs(int x, int y)
{
vis[x][y] = 1;
for (int i = 0; i < 6; i++)
{
int nx = x + dx[i];
int ny = y + dy[i];
// 检查边界
if (nx < 1 || nx > 2000 || ny < 1 || ny > 2000)
{
continue;
}
// 检查该位置是否有雪球
if (a[nx][ny] == 0)
{
continue;
}
// 检查是否已访问
if (vis[nx][ny] == 1)
{
continue;
}
vis[nx][ny] = 1;
dfs(nx, ny);
}
}
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
{
int x, y;
cin >> x >> y;
// 坐标偏移,避免负坐标
x += 1000, y += 1000;
a[x][y] = 1; // 标记雪球位置
}
// 遍历整个网格
for (int i = 0; i <= 2000; i++)
{
for (int j = 0; j <= 2000; j++)
{
// 如果这个位置有雪球且未访问过
if (a[i][j] == 1 && vis[i][j] == 0)
{
dfs(i, j);
cnt++; // 发现一个新的连通块
}
}
}
cout << cnt << endl;
return 0;
}
【运行结果】
6
-1 -1
0 1
0 2
1 0
1 2
2 0
3
浙公网安备 33010602011771号