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

第1题:问题求解

给定一个正整数N,求最小的M满足比N大且M与N的二进制表示中有相同数目的1。

举个例子,假如给定N为78,二进制表示为1001110,包含4个1,那么最小的比N大的并且二进制表示中只包含4个1的数是83,其二进制是1010011,因此83就是答案。

时间限制:1000

内存限制:65536
输入

输入若干行,每行一个数N(1 ≤ N ≤ 1000000),如果这行为0表示输入结束。
输出

对于每个N,输出对应的M。
样例输入

1

2

3

4

78

0
样例输出

2

4

5

8

83

这个问题可以使用搜索剪枝技术来解决。我们可以从给定的整数N开始,逐个尝试比N大的整数,直到找到一个满足条件的整数M。在每次尝试时,我们需要判断M的二进制表示中是否包含与N相同数量的1。如果找到了满足条件的M,则输出M作为答案。

下面是一个使用搜索剪枝技术的示例代码:

c 复制代码
#include <stdio.h>

// 计算一个整数的二进制表示中1的数量
int countOnes(int num) {
    int count = 0;
    while (num) {
        count += num & 1;
        num >>= 1;
    }
    return count;
}

// 判断一个整数的二进制表示中1的数量是否与给定的count相等
int hasSameNumberOfOnes(int num, int count) {
    return countOnes(num) == count;
}

// 搜索比N大且二进制表示中包含count个1的整数
int findNextNumber(int N, int count) {
    int num = N + 1;
    while (!hasSameNumberOfOnes(num, count)) {
        num++;
    }
    return num;
}

int main() {
    int N;
    while (scanf("%d", &N) == 1 && N != 0) {
        int count = countOnes(N);
        int M = findNextNumber(N, count);
        printf("%d\n", M);
    }
    return 0;
}

这段代码定义了三个函数:countOnes用于计算一个整数的二进制表示中1的数量,hasSameNumberOfOnes用于判断一个整数的二进制表示中1的数量是否与给定的count相等,findNextNumber用于搜索比N大且二进制表示中包含count个1的整数。

main函数中,首先读取输入的整数N,然后通过调用countOnes函数计算N的二进制表示中1的数量。接下来,调用findNextNumber函数找到比N大且二进制表示中包含相同数量的1的整数M。最后,输出M作为答案。

你可以将题目给出的示例输入复制到代码中进行测试。

第2题:算24

给出4个小于10个正整数,你可以使用加减乘除4种运算以及括号把这4个数连接起来得到一个表达式。现在的问题是,是否存在一种方式使得得到的表达式的结果等于24。 这里加减乘除以及括号的运算结果和运算的优先级跟我们平常的定义一致(这里的除法定义是实数除法)。 比如,对于5,5,5,1,我们知道5 * (5 -- 1 / 5) = 24,因此可以得到24。又比如,对于1,1,4,2,我们怎么都不能得到24。

时间限制:6000

内存限制:65536
输入

输入数据包括多行,每行给出一组测试数据,包括4个小于10个正整数。最后一组测试数据中包括4个0,表示输入的结束,这组数据不用处理。
输出

对于每一组测试数据,输出一行,如果可以得到24,输出"YES";否则,输出"NO"。
样例输入

5 5 5 1

1 1 4 2

0 0 0 0
样例输出

YES

NO

这个问题可以使用搜索剪枝技术来解决。我们可以尝试对给定的4个数字进行各种运算和排列组合,判断是否存在一种方式使得表达式的结果等于24。在每次尝试时,我们可以使用递归来遍历所有可能的运算和数字排列,并进行剪枝操作以提高效率。

下面是一个使用搜索剪枝技术的示例代码:

c 复制代码
#include <stdio.h>
#include <stdbool.h>

#define TARGET 24
#define EPSILON 1e-6

bool canGet24(double nums[], int count) {
    if (count == 1) {
        return fabs(nums[0] - TARGET) < EPSILON;
    }

    for (int i = 0; i < count; i++) {
        for (int j = 0; j < count; j++) {
            if (i == j) {
                continue;
            }

            double a = nums[i];
            double b = nums[j];

            double nextNums[count - 1];
            int nextCount = 0;

            for (int k = 0; k < count; k++) {
                if (k != i && k != j) {
                    nextNums[nextCount++] = nums[k];
                }
            }

            nextNums[nextCount] = a + b;
            if (canGet24(nextNums, nextCount + 1)) {
                return true;
            }

            nextNums[nextCount] = a - b;
            if (canGet24(nextNums, nextCount + 1)) {
                return true;
            }

            nextNums[nextCount] = b - a;
            if (canGet24(nextNums, nextCount + 1)) {
                return true;
            }

            nextNums[nextCount] = a * b;
            if (canGet24(nextNums, nextCount + 1)) {
                return true;
            }

            if (b != 0) {
                nextNums[nextCount] = a / b;
                if (canGet24(nextNums, nextCount + 1)) {
                    return true;
                }
            }

            if (a != 0) {
                nextNums[nextCount] = b / a;
                if (canGet24(nextNums, nextCount + 1)) {
                    return true;
                }
            }
        }
    }

    return false;
}

int main() {
    double nums[4];
    while (scanf("%lf %lf %lf %lf", &nums[0], &nums[1], &nums[2], &nums[3]) == 4) {
        if (nums[0] == 0 && nums[1] == 0 && nums[2] == 0 && nums[3] == 0) {
            break;
        }

        if (canGet24(nums, 4)) {
            printf("YES\n");
        } else {
            printf("NO\n");
        }
    }

    return 0;
}

这段代码定义了一个canGet24函数,用于判断给定的4个数字是否可以通过运算得到24。在函数内部,使用两层循环遍历所有可能的数字组合,并针对每个组合进行各种运算操作。在每次运算后,将得到的结果与24进行比较,如果结果接近24(使用浮点数比较,并定义了一个极小的误差范围EPSILON),则递归调用canGet24函数继续判断剩余的数字是否可以得到24。如果最终找到一种方式使得结果等于24,返回true;否则,返回false

main函数中,首先读取输入的4个数字,然后通过调用canGet24函数判断是否可以得到24,并输出相应的结果。

你可以将题目给出的示例输入复制到代码中进行测试。

第3题:忍者道具

忍者道具有很多种,苦无,飞镖,震爆弹。L君热衷于收集忍者道具,现在他有N个道具,每个道具的重量分别是C1、C2...CN。现在他想把这N个道具装到载重量为W的工具包里,请问他最少需要多少个工具包?

时间限制:1000

内存限制:65536
输入

第一行包含两个用空格隔开的整数,N和W。 接下来N行每行一个整数,其中第i+1行的整数表示第i个道具的重量Ci。
输出

输出一个整数,最少需要多少个工具包。
样例输入

5 1996

1

2

1994

12

29
样例输出

2

提示

对于100%的数据,1<=N<=18,1<=Ci<=W<=10^8。

这个问题可以使用贪心算法来解决。我们可以按照道具的重量从大到小进行排序,然后依次将道具放入工具包中,直到当前道具无法放入为止。然后再取下一个道具放入工具包,重复这个过程,直到所有道具都被放入工具包中。最后统计使用的工具包数量即可。

下面是一个使用贪心算法的示例代码:

c 复制代码
#include <stdio.h>
#include <stdlib.h>

// 比较函数,用于排序
int compare(const void* a, const void* b) {
    return *(int*)b - *(int*)a;
}

int minToolkits(int weights[], int N, int W) {
    qsort(weights, N, sizeof(int), compare); // 按照重量从大到小排序

    int count = 0; // 工具包数量
    int currentWeight = 0; // 当前工具包中的总重量

    for (int i = 0; i < N; i++) {
        if (currentWeight + weights[i] > W) {
            // 当前道具无法放入当前工具包,需要使用新的工具包
            count++;
            currentWeight = 0;
        }

        currentWeight += weights[i];
    }

    // 最后一个工具包可能未达到最大载重,但仍然需要计算在内
    if (currentWeight > 0) {
        count++;
    }

    return count;
}

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

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

    int result = minToolkits(weights, N, W);
    printf("%d\n", result);

    return 0;
}

这段代码定义了一个minToolkits函数,用于计算最少需要多少个工具包来装载道具。在函数内部,首先使用qsort函数对道具的重量进行排序,按照从大到小的顺序。然后,使用贪心策略,依次将道具放入工具包中,如果当前道具无法放入当前工具包,则使用新的工具包。最后,统计使用的工具包数量并返回。

main函数中,首先读取输入的N和W,然后读取每个道具的重量,并将它们存储在weights数组中。接下来,调用minToolkits函数计算最少需要的工具包数量,并输出结果。

你可以将题目给出的示例输入复制到代码中进行测试。

第4题:泳池

小C在一个排水系统不太好的学校上学。又是一个下雨天,学校里高低不平积了很多水。小C突发奇想:如果大雨一直下,多久以后我可以在学校里游泳呢?

学校是 N x N 的坐标方格 grid 中,每一个方格的值 grid(i,j)表示在位置 (i,j) 的高度。现在开始下雨了。当时间为 t 时,此时雨水导致方格中任意位置的水位为 t 。你可以从一个方格游向四周相邻的任意一个方格,但是前提是此时水位必须同时淹没这两个方格。假定小C的游动是不耗时的。

现在小C从坐标方格的左上(0,0)出发。最少耗时多久他才能到达坐标方格的右下平台 (N-1, N-1)?

时间限制:1000

内存限制:65536
输入

第一行有一个整数N,以下是一个N*N的方阵,代表各处的高度。 输入范围: 2 ≤ N ≤ 300 0 ≤ Height ≤ 10000000
输出

输出一个整数,代表最少等待时间T
样例输入

样例输入1:

2

0 2

1 3

样例输入2:

5

0 1 2 3 4

24 23 22 21 5

12 13 14 15 16

11 17 18 19 20

10 9 8 7 6
样例输出

样例输出1:

3

样例输出2:

16
提示

样例1:时间为3时,才可以游向平台(1,1),此时水位为3。 样例2:时间为16时,水位为16,此时才能保证(0,0)和(4,4)是联通的(请自行找出一条通路)。

这个问题可以使用搜索剪枝技术来解决。我们可以使用深度优先搜索(DFS)来搜索所有可能的路径,并在搜索过程中进行剪枝,以减少搜索空间。

下面是一个使用搜索剪枝技术的示例代码:

c 复制代码
#include <stdio.h>
#include <stdlib.h>

#define MAX_N 300

int N;
int grid[MAX_N][MAX_N];
int visited[MAX_N][MAX_N];

int minTime(int x, int y, int t) {
    if (x == N - 1 && y == N - 1) {
        return t;
    }

    visited[x][y] = 1;

    int min = -1;

    // 搜索上方
    if (x > 0 && !visited[x - 1][y] && grid[x - 1][y] >= t) {
        int time = minTime(x - 1, y, t + 1);
        if (time != -1 && (min == -1 || time < min)) {
            min = time;
        }
    }

    // 搜索下方
    if (x < N - 1 && !visited[x + 1][y] && grid[x + 1][y] >= t) {
        int time = minTime(x + 1, y, t + 1);
        if (time != -1 && (min == -1 || time < min)) {
            min = time;
        }
    }

    // 搜索左边
    if (y > 0 && !visited[x][y - 1] && grid[x][y - 1] >= t) {
        int time = minTime(x, y - 1, t + 1);
        if (time != -1 && (min == -1 || time < min)) {
            min = time;
        }
    }

    // 搜索右边
    if (y < N - 1 && !visited[x][y + 1] && grid[x][y + 1] >= t) {
        int time = minTime(x, y + 1, t + 1);
        if (time != -1 && (min == -1 || time < min)) {
            min = time;
        }
    }

    visited[x][y] = 0;

    return min;
}

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

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

    int result = minTime(0, 0, 0);
    printf("%d\n", result);

    return 0;
}

这段代码定义了一个minTime函数,用于计算小C到达右下平台的最少等待时间。在函数内部,我们使用深度优先搜索(DFS)来搜索所有可能的路径。对于每个位置(x, y),我们判断是否可以从当前位置游向上、下、左、右四个方向,并将其未访问过且高度不低于当前时间的位置加入搜索路径中。递归地搜索下去,直到到达右下平台或者无法继续搜索。如果找到一条路径,我们记录下最小的等待时间。在搜索过程中,使用visited数组来标记已经访问过的位置,避免重复访问。

main函数中,首先读取输入的N和每个方格的高度,并将它们存储在grid数组中。然后,调用minTime函数计算最少等待时间,并输出结果。

你可以将题目给出的示例输入复制到代码中进行测试。

相关推荐
南宫生11 分钟前
力扣-数据结构-3【算法学习day.74】
java·数据结构·学习·算法·leetcode
挥剑决浮云 -16 分钟前
STM32学习之 按键/光敏电阻 控制 LED/蜂鸣器
c语言·经验分享·stm32·单片机·嵌入式硬件·学习
zfenggo18 分钟前
c/c++ 无法跳转定义
c语言·开发语言·c++
图灵猿22 分钟前
【Lua之·Lua与C/C++交互·Lua CAPI访问栈操作】
c语言·c++·lua
A懿轩A1 小时前
C/C++ 数据结构与算法【树和二叉树】 树和二叉树,二叉树先中后序遍历详细解析【日常学习,考研必备】带图+详细代码
c语言·数据结构·c++·学习·二叉树·
-$_$-1 小时前
【LeetCode 面试经典150题】详细题解之滑动窗口篇
算法·leetcode·面试
hjxxlsx1 小时前
探索 C++ 自定义函数的深度与广度
开发语言·c++
Channing Lewis1 小时前
算法工程化工程师
算法
帅逼码农2 小时前
有限域、伽罗瓦域、扩域、素域、代数扩张、分裂域概念解释
算法·有限域·伽罗瓦域
Jayen H2 小时前
【优选算法】盛最多水的容器
算法