2022年12月 C/C++(三级)真题解析#中国电子学会#全国青少年软件编程等级考试

2022年12月 C/C++(三级)真题解析#中国电子学会#全国青少年软件编程等级考试_第1张图片

第1题:鸡兔同笼

一个笼子里面关了鸡和兔子(鸡有2只脚,兔子有4只脚,没有例外)。已经知道了笼子里面脚的总数a,问笼子里面至少有多少只动物,至多有多少只动物。
时间限制:1000
内存限制:65536
输入
一行,一个正整数a (a < 32768)。
输出
一行,包含两个正整数,第一个是最少的动物数,第二个是最多的动物数,两个正整数用一个空格分开。 如果没有满足要求的答案,则输出两个0,中间用一个空格分开。
样例输入
20
样例输出
5 10

这个问题可以通过数学方法来解决。我们知道鸡有2只脚,兔子有4只脚,且没有例外。假设笼子里的动物数为x,则鸡的数量为y,兔子的数量为z。

根据题目条件,我们可以得到以下两个等式:

(1)2y + 4z = a (脚的总数为a)

(2)y + z = x (动物的总数为x)

我们需要求解的是最少和最多的动物数,即求解x的最小值和最大值。

最少动物数的情况下,我们假设所有的动物都是兔子,此时每只动物有4只脚。因此,脚的总数a的最小值必须是4的倍数。在这种情况下,鸡的数量y为0,兔子的数量z为a/4。

最多动物数的情况下,我们假设所有的动物都是鸡,此时每只动物有2只脚。因此,脚的总数a的最大值必须是2的倍数。在这种情况下,兔子的数量z为0,鸡的数量y为a/2。

综上所述,最少动物数为a/4,最多动物数为a/2。但是需要注意的是,最少和最多动物数必须是整数,且最少动物数不能大于最多动物数。

下面是使用 C 语言编写的解题代码:

#include 

int main() {
    int a;
    scanf("%d", &a);

    int minAnimals = a / 4;
    int maxAnimals = a / 2;

    if (a % 4 != 0) {
        minAnimals++;
    }

    if (minAnimals > maxAnimals) {
        minAnimals = maxAnimals = 0;
    }

    printf("%d %d\n", minAnimals, maxAnimals);

    return 0;
}

在上述代码中,我们首先读取输入的脚的总数 a。然后,我们根据数学推导计算最少和最多的动物数,并将结果输出。

需要注意的是,我们在计算最少动物数时,如果 a 不能被4整除,需要将最少动物数加1,以确保最少动物数为整数。另外,如果最少动物数大于最多动物数,表示没有满足要求的答案,将最少和最多动物数都设置为0。

编译并运行上述的代码可以解决该问题。你可以使用 C 语言编译器将代码保存为 .c 文件,然后编译并运行该文件来获取结果。

第2题:猴子吃桃

海滩上有一堆桃子,N只猴子来分。第一只猴子把这堆桃子平均分为N份,多了一个,这只猴子把多的一个扔入海中,拿走了一份。第二只猴子接着把剩下的桃子平均分成N份,又多了一个,它同样把多的一个扔入海中,拿走了一份。第三、第四、……,第N只猴子仍是最终剩下的桃子分成N份,扔掉多了的一个,并拿走一份。
编写程序,输入猴子的数量N,输出海滩上最少的桃子数,使得每只猴子都可吃到桃子。
时间限制:3000
内存限制:65536
输入
一个整数N。
输出
输出当猴子数量为N时海滩上最少的桃子数。结果保证在int型范围内。
样例输入
2
样例输出
7

这个问题可以使用逆推的思路来解决。我们假设最后剩下的桃子数为x,根据题目描述,可以得到以下等式:

x = (N-1) * (x/N) - 1

根据上述等式,我们可以从最后一只猴子开始逆推,每次逆推得到的桃子数都是上一只猴子分完后剩下的桃子数。

下面是使用 C 语言编写的解题代码:

#include 

int findMinPeaches(int N) {
    int peaches = N;  // 假设最后剩下的桃子数为N

    while (1) {
        int total = peaches;  // 当前剩下的桃子数
        int i;
        for (i = 0; i < N; i++) {
            if (total % (N-1) != 0) {
                // 无法平均分配,不满足条件,增加桃子数并重新开始逆推
                peaches++;
                break;
            }
            total = (N-1) * (total/N) - 1;
        }

        if (i == N) {
            // 找到满足条件的最小桃子数
            return peaches;
        }
    }
}

int main() {
    int N;
    scanf("%d", &N);

    int minPeaches = findMinPeaches(N);
    printf("%d\n", minPeaches);

    return 0;
}

在上述代码中,我们定义了一个函数 findMinPeaches 来逆推找到满足条件的最小桃子数。在函数中,我们假设最后剩下的桃子数为N,然后从最后一只猴子开始逆推,每次逆推得到的桃子数都是上一只猴子分完后剩下的桃子数。我们使用一个循环来进行逆推,直到找到满足条件的最小桃子数。

main 函数中,我们读取输入的猴子数量 N,然后调用 findMinPeaches 函数来计算最小桃子数,并将结果输出。

需要注意的是,该方法是通过逆推来找到最小桃子数的,因此在某些情况下可能会有一定的时间复杂度。对于较大的 N 值,可能需要较长的计算时间。

编译并运行上述的代码可以解决该问题。你可以使用 C 语言编译器将代码保存为 .c 文件,然后编译并运行该文件来获取结果。

第3题:扩号匹配问题

在某个字符串(长度不超过100)中有左括号、右括号和大小写字母;规定(与常见的算数式子一样)任何一个左括号都从内到外与在它右边且距离最近的右括号匹配。写一个程序,找到无法匹配的左括号和右括号,输出原来字符串,并在下一行标出不能匹配的括号。不能匹配的左括号用"$“标注,不能匹配的右括号用”?“标注.
时间限制:3000
内存限制:65536
输入
输入包括多组数据,每组数据一行,包含一个字符串,只包含左右括号和大小写字母,字符串长度不超过100
输出
对每组输出数据,输出两行,第一行包含原始输入字符,第二行由”$“,”?“和空格组成,”$“和”?"表示与之对应的左括号和右括号不能匹配。
样例输入
((ABCD(x)
)(rttyy())sss)(
样例输出
((ABCD(x)
$$
)(rttyy())sss)(
? ?$

这个问题可以使用栈来解决。我们可以遍历字符串中的每个字符,并将左括号入栈,当遇到右括号时,判断栈顶元素是否为左括号,如果是,则将栈顶元素出栈,表示匹配成功;如果不是,则将当前右括号标记为无法匹配的右括号。

在遍历完整个字符串后,栈中可能还有剩余的左括号,这些左括号都是无法匹配的左括号。

下面是使用 C 语言编写的解题代码:

#include 
#include 

#define MAX_SIZE 100

void printUnmatchedParentheses(char* str) {
    int len = strlen(str);
    int stack[MAX_SIZE];  // 用于存储左括号的栈
    int top = -1;  // 栈顶指针

    for (int i = 0; i < len; i++) {
        if (str[i] == '(') {
            // 遇到左括号,入栈
            stack[++top] = i;
        } else if (str[i] == ')') {
            // 遇到右括号
            if (top >= 0 && str[stack[top]] == '(') {
                // 栈不为空且栈顶元素为左括号,匹配成功,栈顶元素出栈
                top--;
            } else {
                // 无法匹配的右括号
                str[i] = '?';
            }
        }
    }

    while (top >= 0) {
        // 未匹配的左括号
        str[stack[top--]] = '$';
    }

    printf("%s\n", str);

    for (int i = 0; i < len; i++) {
        if (str[i] == '$' || str[i] == '?') {
            printf("%c", str[i]);
        } else {
            printf(" ");
        }
    }

    printf("\n");
}

int main() {
    char str[MAX_SIZE];

    while (scanf("%s", str) != EOF) {
        printUnmatchedParentheses(str);
    }

    return 0;
}

在上述代码中,我们定义了一个函数 printUnmatchedParentheses 来打印无法匹配的括号。在函数中,我们使用一个栈来存储左括号的索引。遍历字符串中的每个字符,当遇到左括号时,将其索引入栈;当遇到右括号时,判断栈顶元素是否为左括号,如果是,则将栈顶元素出栈,表示匹配成功;如果不是,则将当前右括号标记为无法匹配的右括号。遍历完整个字符串后,栈中可能还有剩余的左括号,这些左括号都是无法匹配的左括号。

main 函数中,我们循环读取输入的字符串,并调用 printUnmatchedParentheses 函数来打印无法匹配的括号。

编译并运行上述的代码可以解决该问题。你可以使用 C 语言编译器将代码保存为 .c 文件,然后编译并运行该文件来获取结果。

第4题:上台阶

楼梯有n(100 > n > 0)阶台阶,上楼时可以一步上1阶,也可以一步上2阶,也可以一步上3阶,编程计算共有多少种不同的走法。
时间限制:1000
内存限制:65536
输入
输入的每一行包括一组测试数据,即为台阶数n。最后一行为0,表示测试结束。
输出
每一行输出对应一行输入的结果,即为走法的数目。
样例输入
1
2
3
4
0
样例输出
1
2
4
7

这个问题可以使用动态规划来解决。我们可以定义一个数组 dp,其中 dp[i] 表示上 i 阶台阶的不同走法数量。根据题目描述,我们可以得到以下状态转移方程:

dp[i] = dp[i-1] + dp[i-2] + dp[i-3]

其中,dp[i-1] 表示从 i-1 阶走一步到达 i 阶的走法数量,dp[i-2] 表示从 i-2 阶走两步到达 i 阶的走法数量,dp[i-3] 表示从 i-3 阶走三步到达 i 阶的走法数量。因为每次可以选择走一步、两步或三步,所以到达 i 阶的走法数量等于到达 i-1 阶、i-2 阶和 i-3 阶的走法数量之和。

下面是使用 C 语言编写的解题代码:

#include 

#define MAX_SIZE 101

long long countWays(int n) {
    long long dp[MAX_SIZE];

    // 初始化前三个台阶的走法数量
    dp[0] = 0;
    dp[1] = 1;
    dp[2] = 2;
    dp[3] = 4;

    // 计算从第4个台阶开始的走法数量
    for (int i = 4; i <= n; i++) {
        dp[i] = dp[i-1] + dp[i-2] + dp[i-3];
    }

    return dp[n];
}

int main() {
    int n;

    while (1) {
        scanf("%d", &n);

        if (n == 0) {
            break;
        }

        long long ways = countWays(n);
        printf("%lld\n", ways);
    }

    return 0;
}

在上述代码中,我们定义了一个函数 countWays 来计算上 n 阶台阶的不同走法数量。我们使用一个数组 dp 来存储走法数量,初始时将前三个台阶的走法数量进行初始化,然后从第4个台阶开始计算每个台阶的走法数量,直到达到目标台阶 n

main 函数中,我们循环读取输入的台阶数 n,如果输入的 n 为 0,则表示测试结束,退出循环。否则,调用 countWays 函数来计算不同走法数量,并将结果输出。

需要注意的是,题目要求的不同走法数量可能非常大,超过 int 类型的范围,因此我们使用了 long long 类型来存储结果。

编译并运行上述的代码可以解决该问题。你可以使用 C 语言编译器将代码保存为 .c 文件,然后编译并运行该文件来获取结果。

第5题:田忌赛马

在田忌赛马的故事中,孙膑用自己的下等马对战对手的上等马,自己上等马对阵对手的中等马,自己的中等马对阵对手的下等马,从而赢得了胜利。现在即将进行的是N匹马的赛马比赛。双方队伍的马各分为N等。已知只有当我方马的等级比对方马等级高X等以上(包含X)时,我方才可以取得这场比赛的胜利。如果在N场比赛中我方的胜场数大于对方,则我方取得最终的胜利。现在已知对方这N场比赛的出战方案,请计算所有令我方最终获胜的出战方案。
时间限制:1000
内存限制:65536
输入
第一行两个整数,N和X。N≤9, 0 ≤ X < N。 第二行N个正整数,A(1)…A(N)。A(i)表示第i场比赛对方马的等级,1≤i≤N。等级越高越强
输出
按字典序输出所有我方最终获胜的方案,每个方案一行。每行是N个正整数,第i个数表示我方第i场比赛马的等级。
样例输入
样例1输入
3 1
3 2 1
样例2输入
3 0
3 1 2
样例输出
样例1输出
1 3 2
样例2输出
1 2 3
1 3 2
2 1 3
3 1 2
3 2 1

这个问题可以使用回溯法来解决。我们可以使用递归函数来生成所有可能的出战方案,并按字典序输出满足条件的方案。

具体步骤如下:

  1. 创建一个数组 result 用于存储满足条件的出战方案。

  2. 创建一个数组 visited 用于标记每个马是否已经被选中。

  3. 创建一个递归函数 backtrack,该函数接收当前比赛的编号 index,当前已选中的马的等级数组 current,以及对方马的等级数组 opponent

  4. 在递归函数中,首先判断是否已经完成了所有比赛,即 index 是否等于 N。如果是,则将 current 添加到 result 中。

  5. 否则,对于当前比赛的对方马等级 opponent[index],从高等级到低等级依次尝试选择我方马的等级。

  6. 遍历我方马的等级时,根据题目要求判断是否满足条件,如果满足则将该马的等级添加到 current 中,并将对应的 visited 标记为已访问。

  7. 在递归函数中调用下一场比赛,即递归调用 backtrack(index + 1, current, opponent)

  8. 在递归调用返回后,将当前选择的马的等级从 current 中移除,并将对应的 visited 标记为未访问,以便尝试其他选择。

  9. 在主函数中,按字典序输出 result 中的所有方案。

下面是使用 C 语言编写的解题代码:

#include 
#include 
#include 

#define MAX_SIZE 9

void backtrack(int index, int N, int X, int opponent[], int current[], bool visited[], int result[][MAX_SIZE], int* count) {
    if (index == N) {
        memcpy(result[*count], current, sizeof(int) * N);
        (*count)++;
        return;
    }

    for (int i = N - 1; i >= 0; i--) {
        if (!visited[i] && current[index - X] > opponent[index]) {
            visited[i] = true;
            current[index] = i + 1;
            backtrack(index + 1, N, X, opponent, current, visited, result, count);
            visited[i] = false;
        }
    }
}

int main() {
    int N, X;
    scanf("%d %d", &N, &X);

    int opponent[MAX_SIZE];
    for (int i = 0; i < N; i++) {
        scanf("%d", &opponent[i]);
    }

    int result[MAX_SIZE * MAX_SIZE][MAX_SIZE];
    int count = 0;

    int current[MAX_SIZE];
    bool visited[MAX_SIZE] = { false };

    backtrack(0, N, X, opponent, current, visited, result, &count);

    for (int i = 0; i < count; i++) {
        for (int j = 0; j < N; j++) {
            printf("%d ", result[i][j]);
        }
        printf("\n");
    }

    return 0;
}

在上述代码中,我们使用了一个二维数组 result 来存储满足条件的出战方案,其中每一行表示一个方案,每一列表示我方对应比赛的马的等级。我们还定义了一个整数变量 count 来记录满足条件的方案数量。

backtrack 函数中,我们首先判断是否已经完成了所有比赛,如果是,则将当前方案 current 添加到 result 中。否则,对于当前比赛的对方马等级 opponent[index],从高等级到低等级依次尝试选择我方马的等级。遍历我方马的等级时,根据题目要求判断是否满足条件,如果满足则将该马的等级添加到 current中,并将对应的 visited 标记为已访问。然后递归调用下一场比赛,即 backtrack(index + 1, current, opponent)。在递归调用返回后,将当前选择的马的等级从 current 中移除,并将对应的 visited 标记为未访问,以便尝试其他选择。

在主函数中,我们首先读取输入的比赛信息,然后调用 backtrack 函数来生成所有满足条件的方案。最后,按字典序输出 result 中的所有方案。

编译并运行上述的代码可以解决该问题。你可以使用 C 语言编译器将代码保存为 .c 文件,然后编译并运行该文件来获取结果。

你可能感兴趣的:(C/C++等级考试历届真题解析,c语言,c++,算法,等级考试,电子学会)