学而思编程每周练习赛 | 2025年春第2周

附上学而思公众号链接:学而思编程每周练习赛-第02周-习题视频讲解与试题分析

语言基础组

最大面积

【题目来源】

学而思编程:最大面积

【题目描述】

\(n\) 个圆,每个圆的半径依次为 \(r_1,r_2,…,r_n\),请计算求出这 \(n\) 个圆的最大面积,结果四舍五入保留 \(2\) 位小数。

【输入】

第一行,包含一个整数 \(n\)

第二行,包含 \(n\) 个正整数 \(r_1,r_2,…,r_n\),依次表示每个圆的半径。

【输出】

一行,一个实数,结果四舍五入保留 \(2\) 位小数。

【输入样例】

3
2 1 3

【输出样例】

28.27

【代码详解】

#include <bits/stdc++.h>
using namespace std;

const int N = 100;  // 定义常量 N,表示数组的最大长度
int n;              // n 表示输入的数字个数
int a[N];           // 数组 a 用于存储输入的数字(未使用)
double ans = -1e9;  // ans 用于记录最大面积,初始化为一个很小的值

int main()
{
    cin >> n;  // 输入数字的个数 n

    for (int i = 1; i <= n; i++)  // 遍历每个数字
    {
        int x;
        cin >> x;  // 输入当前数字 x

        // 计算当前数字对应的圆的面积,并更新最大值
        ans = max(ans, 1.0 * x * x * 3.1415926);
    }

    printf("%.2lf\n", ans);  // 输出最大面积,保留两位小数
    return 0;
}

【运行结果】

3
2 1 3
28.27

双重质数

【题目来源】

学而思编程:双重质数

【题目描述】

正整数可分为:单位(即 \(1\))、质数与合数三类。每个质数有且仅有两个因数(\(1\) 和它本身)。注意 \(1\) 既不是质数,也不是合数。

有一些数,它自身是质数,在十进制表示下,如果去掉它的个位数,仍然是一个质数。我们称这种质数为双质数

例如,\(23\)\(593\)\(1931\) 都是双质数。而 \(17\) 不是,因为 \(1\) 不算质数。

给定两个正整数 \(a\)\(b\)。请在 \(a\)\(b\) 的范围内(包含 \(a\)\(b\)),寻找并输出所有的双质数

【输入】

两个正整数表示 \(a\)\(b\)

【输出】

若干行,每行输出一个双质数,如果一个都没有,则输出 None

【输入样例】

20 50

【输出样例】

23
29
31
37

【代码详解】

#include <bits/stdc++.h>
using namespace std;

int a, b;  // 定义变量 a 和 b,表示范围的起点和终点

// 判断一个数是否为质数的函数
bool isPrime(int x)
{
    if (x < 2) return false;  // 小于 2 的数不是质数
    for (int i = 2; i <= x / i; i++)  // 遍历 2 到 sqrt(x)
        if (x % i == 0) return false;  // 如果 x 能被 i 整除,则 x 不是质数
    return true;  // 否则 x 是质数
}

int main()
{
    cin >> a >> b;  // 输入范围的起点 a 和终点 b

    // 遍历范围 [a, b] 中的每个数
    for (int i = a; i <= b; i++)
    {
        // 如果当前数 i 是质数,且去掉个位数后的数也是质数
        if (isPrime(i) && isPrime(i / 10))
            cout << i << endl;  // 输出当前数 i
    }

    return 0;
}

【运行结果】

20 50
23
29
31
37

xyz

【题目来源】

学而思编程:xyz

【题目描述】

有三个正整数 \(x,y,z\),我们不知道每个数的具体值,但我们知道 \(x≤y≤z\)

现在,以随机顺序给出 \(x+y,x+z,y+z,x+y+z\) 的值,请你求出 \(x,y,z\) 的值。

题目保证一定有解。

【输入】

共一行,包含四个整数 \(x_1,x_2,x_3,x_4\),表示以随机顺序给出的 \(x+y,x+z,y+z,x+y+z\) 的值。

【输出】

共一行,三个空格隔开的整数 \(x,y,z\)

【输入样例】

3 6 5 4

【输出样例】

1 2 3

【代码详解】

#include <bits/stdc++.h>
using namespace std;

const int N = 5;  // 定义常量 N,表示数组的最大长度
int a[N];         // 定义数组 a,用于存储输入的 4 个数

int main()
{
    // 输入 4 个数
    for (int i = 1; i <= 4; i++)
        cin >> a[i];

    // 对数组 a 的第 1 到第 4 个元素进行升序排序
    sort(a + 1, a + 5);

    // 输出结果:a[4] - a[3], a[4] - a[2], a[4] - a[1]
    cout << a[4] - a[3] << " " << a[4] - a[2] << " " << a[4] - a[1] << endl;

    return 0;
}

【运行结果】

3 6 5 4
1 2 3

普及奠基组

字符串加密

【题目来源】

学而思编程:字符串加密

【题目描述】

在密码学中,我们称需要加密的信息为明文,用 \(M\) 表示;称加密后的信息为密文,用 \(C\) 表示;而密钥是一种参数,是将明文转换为密文或将密文转换为明文的算法中输入的数据,记为 \(k\)

上一次,小猴的加密方式很快就被破解掉了,后来他研究了一种简单的加密算法,用来处理只包含大小写英文字符的字符串:

  • 首先,将字符串中的字母进行大小转换,即将大写字母改成对应小写字母,将小写字母改成对应大写字母;
  • 然后,将字符串中第一个字母与最后一个字母对调,第二个字母与倒数第二个字母对调,其他字母以此类推。显然,对调字母的次数等于字符串长度的一半(向下取整)。

现在,给定一个已经被小猴加密过的字符串 \(C\),请你解密出原来的字符串 \(M\)

【输入】

一行,包含一个字符串 \(C\)

【输出】

一行,包含一个字符串,表示经过解密的内容。

【输入样例】

EDOcUOhOAIx

【输出样例】

XiaoHouCode

【解题思路】

image

【代码详解】

#include <bits/stdc++.h>
using namespace std;

string s;  // s 表示输入的字符串

int main()
{
    // 输入字符串
    cin >> s;

    // 遍历字符串的每个字符
    for (int i = 0; i < s.length(); i++)
    {
        // 如果字符是小写字母,转换为大写字母
        if (s[i] >= 'a' && s[i] <= 'z')
            s[i] = s[i] - 32;

        // 如果字符是大写字母,转换为小写字母
        else if (s[i] >= 'A' && s[i] <= 'Z')
            s[i] = s[i] + 32;
    }

    // 反转字符串
    reverse(s.begin(), s.end());

    // 输出处理后的字符串
    cout << s << endl;

    return 0;
}

【运行结果】

EDOcUOhOAIx
XiaoHouCode

美丽的数

【题目来源】

学而思编程:美丽的数

【题目描述】

如果一个正整数除了 \(1\) 和它本身之外,有且只有一个约数,则称此正整数是“美丽的”。

现在有 \(q\) 次询问,每次询问给定一个正整数 \(x_i\),请你判断 \(x_i\) 是否为一个“美丽的数”,如果 \(x_i\) 是美丽的数,则输出 Yes,否则输出 No

【输入】

第一行,包含一个正整数 \(q\)

接下来 \(q\) 行,每行包含一个正整数 \(x_i\),第 \(i\) 行的正整数为 \(x_i\)

【输出】

\(q\) 行,其中第 \(i\) 行输出对 \(x_i\) 的判断结果,如果 \(x_i\) 是美丽的数,则输出 Yes,否则输出 No

【输入样例】

3
4
5
6

【输出样例】

Yes
No
No

【解题思路】

image

【代码详解】

#include <bits/stdc++.h>
#include <cmath>
using namespace std;

// 判断一个数是否是质数
bool isPrime(long long n)
{
    if (n < 2) return false;  // 小于 2 的数不是质数
    for (int i = 2; i * i <= n; i++)  // 遍历 2 到 sqrt(n)
    {
        if (n % i == 0) return false;  // 如果 n 能被 i 整除,则 n 不是质数
    }
    return true;  // 否则 n 是质数
}

// 判断一个数是否是平方质数
bool check(long long n)
{
    long long t = sqrt(n);  // 计算 n 的平方根
    if (t * t != n) return false;  // 如果 t * t 不等于 n,则 n 不是完全平方数
    return isPrime(t);  // 判断平方根 t 是否是质数
}

int main()
{
    int n;  // n 表示测试用例的数量
    cin >> n;

    // 处理每个测试用例
    while (n--)
    {
        long long m;  // m 表示需要判断的数
        cin >> m;

        // 判断 m 是否是平方质数,并输出结果
        if (check(m)) cout << "Yes" << endl;
        else cout << "No" << endl;
    }

    return 0;
}

【运行结果】

3
4
Yes
5
No
6
No

全都为1*

【题目来源】

学而思编程:全都为1

【题目描述】

小猴和朋友正在玩一个游戏,初始时,在一个 \(n×n\) 的棋盘上放置着 \(n×n\) 个棋子,棋子只有黑色和白色两种,其中棋盘左上角的坐标为 \((1,1)\),右下角的坐标为 \((n,n)\)

小猴可以进行任意次操作,每次操作小猴会在棋盘内选择一个位置 \((x,y)\),然后将该位置与棋盘右下角 \((n,n)\) 形成的矩形范围内所有棋子进行更换为颜色相反的棋子,即白色棋子换成黑色棋子,黑色棋子换成白色棋子。

我们使用数字 \(0\) 代表白色棋子,数字 \(1\) 代表黑色棋子,则有如下例子:

image

如上图,选择坐标为 \((3,2)\) 的位置,这样就将整个棋盘的棋子全部变成黑色。

请问小猴最少需要操作多少次,才能使得整个 \(n×n\) 的棋盘中的所有棋子都为黑色。

【输入】

第一行,包含一个整数 \(n\)

接下来 \(n\) 行,每行 \(n\) 个整数 \(a_{i,1},a_{i,2},…,a_{i,n}\),其中 \(a_{i,j}\) 表示棋盘位置坐标为 \((i,j)\) 的棋子颜色,\(0\) 表示白色,\(1\) 表示黑色。

【输出】

一行,包含一个整数,表示小猴最少需要操作的步数。

【输入样例】

4
1 1 1 1
1 1 1 1
1 0 0 0
1 0 0 0

【输出样例】

1

【解题思路】

image

【代码详解】

#include <bits/stdc++.h>
using namespace std;

int n;              // n 表示矩阵的大小
int a[2005][2005];  // a[i][j] 表示矩阵的第 i 行第 j 列的元素
int f[2005][2005];  // f[i][j] 表示从 (1,1) 到 (i,j) 的子矩阵中满足条件的元素数量

int main()
{
    // 输入矩阵的大小
    cin >> n;

    // 输入矩阵的元素
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            cin >> a[i][j];
        }
    }

    // 动态规划求解
    for (int i = 1; i <= n; i++)
    {
        for (int j = 1; j <= n; j++)
        {
            // 计算 f[i][j] 的初始值
            f[i][j] = f[i - 1][j] + f[i][j - 1] - f[i - 1][j - 1];

            // 根据 f[i-1][j] + f[i][j-1] - f[i-1][j-1] 的奇偶性更新 f[i][j]
            if (f[i][j] % 2 == 1)  // 如果是奇数
            {
                if (a[i][j] == 1) f[i][j]++;  // 如果 a[i][j] 是 1,则 f[i][j] 加 1
            }
            else  // 如果是偶数
            {
                if (a[i][j] == 0) f[i][j]++;  // 如果 a[i][j] 是 0,则 f[i][j] 加 1
            }
        }
    }

    // 输出结果
    cout << f[n][n] << endl;

    return 0;
}

【运行结果】

4
1 1 1 1
1 1 1 1
1 0 0 0
1 0 0 0
1

普及进阶组

拿香蕉(强)

【题目来源】

学而思编程:拿香蕉(强)

【题目描述】

小猴的桌子上放着 \(n\) 个香蕉从左到右排成一列,编号为从 \(1\)\(n\)

小美是小猴的好朋友,每天她都会从中拿走一些香蕉。

每天在拿的时候,小美都是从左侧第 \(1\) 个香蕉开始,把第 \(1,3,5,…,2k−1(2k−1≤n)\) 个香蕉拿走。随后小美会将剩下的香蕉按原先的顺序重新排成一列。

小美想知道,编号为 \(n\) 的香蕉是在第几天被拿走的?

【输入】

输入的第一行包含一个正整数 \(n\),表示香蕉的总数。

【输出】

输出一行包含一个正整数,表示小美拿走编号为 \(n\) 的香蕉是在第几天。

【输入样例】

6

【输出样例】

2

【代码详解】

#include <bits/stdc++.h>
using namespace std;

const int N = 5005;  // 定义常量 N,表示数组的最大长度
int A[N], b, r;      // A 数组用于存储大整数,b 是除数,r 是余数

// 高精度除法函数:A / b = C ... r
void div(int A[], int b, int C[], int& r) {
    r = 0;  // 初始化余数为 0
    for (int i = A[0]; i > 0; --i) {  // 从高位到低位遍历 A
        r = r * 10 + A[i];  // 计算当前位的被除数
        C[i] = r / b;       // 计算当前位的商
        r %= b;             // 计算当前位的余数
    }
    // 确定商的位数
    C[0] = A[0];  // 商的位数初始化为 A 的位数
    while (C[0] > 1 && C[C[0]] == 0) --C[0];  // 去掉商的前导 0
}

int main()
{
    string a;
    cin >> a;  // 输入大整数的字符串表示
    A[0] = a.length();  // A[0] 存储大整数的位数
    for (int i = 0; i < A[0]; i++) 
        A[A[0] - i] = a[i] - '0';  // 将字符串转换为数组,A[1] 是最低位,A[A[0]] 是最高位

    int ans = 1;  // 初始化结果为 1
    while (A[1] % 2 == 0)  // 如果 A 的最低位是偶数
    {
        div(A, 2, A, r);  // 将 A 除以 2,结果存回 A
        ans++;  // 结果加 1
    }
    cout << ans << endl;  // 输出结果
    return 0;
}

【运行结果】

6
2

对角线数独

【题目来源】

学而思编程:对角线数独

【题目描述】

对角线数独是从数独衍生出的一个变种。在 \(9×9\) 的大九宫格中填入数字 \(1∼9\),使他们满足如下规则:每行、每列和每个小九宫格内不能出现一样的数字,在每条大对角线中也不能出现一样的数字。

以下是一个例子:

image

【输入】

输入共 \(9\) 行,每行 \(9\) 个用空格隔开的一位整数,这些数展示了需要填写完成的数独表格。

保证每个数都是 \(0∼9\) 范围内的正整数,其中 \(0\) 表示需要填写。

注意一个正确的数独题的解是存在且唯一的,本题的数据也保证这一点。

【输出】

输出共 \(9\) 行,每行 \(9\) 个用空格隔开的整数,这些数展示了整个填写完成的数独表格。

每个数应都是 \(1∼9\) 范围内的正整数。

【输入样例】

6 7 0 0 0 9 0 8 0
0 0 0 0 5 0 9 0 3
0 0 0 0 0 6 0 0 0
0 0 9 0 0 0 0 5 0
0 0 0 0 0 4 0 7 9
4 0 0 0 6 0 0 0 2
0 4 0 6 9 0 5 0 0
0 0 0 0 0 0 0 0 6
0 0 0 8 7 3 0 0 0

【输出样例】

6 7 5 3 2 9 1 8 4
8 2 1 4 5 7 9 6 3
9 3 4 1 8 6 7 2 5
3 6 9 7 1 2 4 5 8
1 8 2 5 3 4 6 7 9
4 5 7 9 6 8 3 1 2
2 4 8 6 9 1 5 3 7
7 1 3 2 4 5 8 9 6
5 9 6 8 7 3 2 4 1

【代码详解】

#include <bits/stdc++.h>
using namespace std;

int a[15][15]; // 定义一个15x15的二维数组,用于存储数独的初始状态

// 检查在 (x, y) 位置填入 num 是否合法
bool isValid(int x, int y, int num) {
    // 检查行
    for (int i = 0; i < 9; i++) {
        if (a[x][i] == num) return false; // 如果同一行存在相同的数字,返回false
    }
    // 检查列
    for (int i = 0; i < 9; i++) {
        if (a[i][y] == num) return false; // 如果同一列存在相同的数字,返回false
    }
    // 检查 3x3 宫
    int sx = (x / 3) * 3; // 计算3x3宫的起始行
    int sy = (y / 3) * 3; // 计算3x3宫的起始列
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            if (a[sx + i][sy + j] == num) return false; // 如果3x3宫内存在相同的数字,返回false
        }
    }
    // 检查主对角线
    if (x == y) {
        for (int i = 0; i < 9; i++) {
            if (a[i][i] == num) return false; // 如果主对角线上存在相同的数字,返回false
        }
    }
    // 检查副对角线
    if (x + y == 8) {
        for (int i = 0; i < 9; i++) {
            if (a[i][8 - i] == num) return false; // 如果副对角线上存在相同的数字,返回false
        }
    }
    return true; // 如果所有检查都通过,返回true
}

// 找到候选数字最少的空格
pair<int, int> findBestCell() {
    int minCandidates = 10; // 初始化最小候选数字数量为10(大于9)
    pair<int, int> bestCell = {-1, -1}; // 初始化最佳单元格为无效值
    for (int i = 0; i < 9; i++) {
        for (int j = 0; j < 9; j++) {
            if (a[i][j] == 0) { // 如果当前单元格为空
                int candidates = 0;
                for (int num = 1; num <= 9; num++) {
                    if (isValid(i, j, num)) { // 检查每个数字是否可以填入当前单元格
                        candidates++; // 增加候选数字数量
                    }
                }
                if (candidates < minCandidates) { // 如果当前单元格的候选数字数量小于最小值
                    minCandidates = candidates; // 更新最小候选数字数量
                    bestCell = {i, j}; // 更新最佳单元格
                }
            }
        }
    }
    return bestCell; // 返回最佳单元格
}

// 深度优先搜索求解数独
bool dfs() {
    pair<int, int> cell = findBestCell(); // 找到候选数字最少的空格
    if (cell.first == -1) return true; // 如果没有空格,说明数独已解
    int x = cell.first, y = cell.second;
    for (int num = 1; num <= 9; num++) {
        if (isValid(x, y, num)) { // 如果当前数字可以填入当前单元格
            a[x][y] = num; // 填入数字
            if (dfs()) // 递归调用dfs,尝试解决剩余的数独
                return true;
            else 
                a[x][y] = 0; // 如果递归调用失败,回溯,将当前单元格清空
        }
    }
    return false; // 如果所有数字都无法填入当前单元格,返回false
}

int main() {
    // 读取数独初始状态
    for (int i = 0; i < 9; i++) {
        for (int j = 0; j < 9; j++) {
            cin >> a[i][j]; // 从标准输入读取数独的初始状态
        }
    }
    // 求解数独
    if (dfs()) {
        // 输出解
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                cout << a[i][j] << " "; // 输出数独的解
            }
            cout << endl;
        }
    } else {
        cout << "No Solution!" << endl; // 如果没有解,输出"No Solution!"
    }
    return 0;
}

【运行结果】

6 7 0 0 0 9 0 8 0
0 0 0 0 5 0 9 0 3
0 0 0 0 0 6 0 0 0
0 0 9 0 0 0 0 5 0
0 0 0 0 0 4 0 7 9
4 0 0 0 6 0 0 0 2
0 4 0 6 9 0 5 0 0
0 0 0 0 0 0 0 0 6
0 0 0 8 7 3 0 0 0
6 7 5 3 2 9 1 8 4
8 2 1 4 5 7 9 6 3
9 3 4 1 8 6 7 2 5
3 6 9 7 1 2 4 5 8
1 8 2 5 3 4 6 7 9
4 5 7 9 6 8 3 1 2
2 4 8 6 9 1 5 3 7
7 1 3 2 4 5 8 9 6
5 9 6 8 7 3 2 4 1
posted @ 2026-03-21 10:51  团爸讲算法  阅读(1)  评论(0)    收藏  举报