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

C/C++编程(1~8级)全部真题・点这里

第1题:道路

N个以 1 ... N 标号的城市通过单向的道路相连:。每条道路包含两个参数:道路的长度和需要为该路付的通行费(以金币的数目来表示)

Bob and Alice 过去住在城市 1.在注意到Alice在他们过去喜欢玩的纸牌游戏中作弊后,Bob和她分手了,并且决定搬到城市N。他希望能够尽可能快的到那,但是他囊中羞涩。我们希望能够帮助Bob找到从1到N最短的路径,前提是他能够付的起通行费。

时间限制:1000

内存限制:65536
输入

第一行包含一个整数K, 0 <= K <= 10000, 代表Bob能够在他路上花费的最大的金币数。第二行包含整数N, 2 <= N <= 100, 指城市的数目。第三行包含整数R, 1 <= R <= 10000, 指路的数目. 接下来的R行,每行具体指定几个整数S, D, L 和 T来说明关于道路的一些情况,这些整数之间通过空格间隔: S is 道路起始城市, 1 <= S <= N D is 道路终点城市, 1 <= D <= N L is 道路长度, 1 <= L <= 100 T is 通行费 (以金币数量形式度量), 0 <= T <=100 注意不同的道路可能有相同的起点和终点。
输出

输入结果应该只包括一行,即从城市1到城市N所需要的最小的路径长度(花费不能超过K个金币)。如果这样的路径不存在,结果应该输出-1。
样例输入

5

6

7

1 2 2 3

2 4 3 3

3 4 2 4

1 3 4 1

4 6 2 1

3 5 2 0

5 4 3 2
样例输出

11

这个问题可以使用Dijkstra算法来解决,用于找到从城市1到城市N的最短路径。

以下是一个解决道路问题的C语言程序:

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

#define MAX_N 100
#define MAX_R 100

// 定义道路结构
typedef struct {
    int dest;
    int length;
    int toll;
} Road;

int dijkstra(int K, int N, int R, Road roads[][MAX_R]) {
    int distance[MAX_N];
    bool visited[MAX_N];
    int i, j;

    // 初始化距离数组和访问标记数组
    for (i = 1; i <= N; i++) {
        distance[i] = INT_MAX;
        visited[i] = false;
    }

    // 设置起点城市1的距离为0
    distance[1] = 0;

    // 执行N-1次迭代
    for (i = 1; i < N; i++) {
        int minDistance = INT_MAX;
        int minIndex = -1;

        // 找到距离最小且未被访问的城市
        for (j = 1; j <= N; j++) {
            if (!visited[j] && distance[j] < minDistance) {
                minDistance = distance[j];
                minIndex = j;
            }
        }

        if (minIndex == -1) {
            break;  // 所有城市都已访问,退出循环
        }

        visited[minIndex] = true;

        // 更新与当前城市相邻的城市的距离
        for (j = 0; j < R; j++) {
            Road road = roads[minIndex][j];
            if (road.dest != -1 && distance[minIndex] + road.length <= K && distance[minIndex] + road.length + road.toll < distance[road.dest]) {
                distance[road.dest] = distance[minIndex] + road.length + road.toll;
            }
        }
    }

    return distance[N];
}

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

    Road roads[MAX_N][MAX_R];
    int i, j;
    for (i = 1; i <= N; i++) {
        for (j = 0; j < R; j++) {
            roads[i][j].dest = -1;  // 初始化道路目的地为-1表示无效
        }
    }

    for (i = 0; i < R; i++) {
        int S, D, L, T;
        scanf("%d %d %d %d", &S, &D, &L, &T);
        for (j = 0; j < R; j++) {
            if (roads[S][j].dest == -1) {
                roads[S][j].dest = D;
                roads[S][j].length = L;
                roads[S][j].toll = T;
                break;
            }
        }
    }

    int result = dijkstra(K, N, R, roads);
    if (result == INT_MAX) {
        printf("-1\n");
    } else {
        printf("%d\n", result);
    }

    return 0;
}

在这个解决方案中,我们首先读取输入的金币数K、城市数N和道路数R。

然后,我们创建一个大小为MAX_N×MAX_R的二维数组roads,用于存储道路的信息。

接下来,我们初始化roads数组的每个元素的目的地为-1,表示无效。然后,我们根据输入的道路信息,将相应的目的地、长度和通行费存储在roads数组中。

然后,我们调用dijkstra函数进行Dijkstra算法的执行。该函数接受金币数K、城市数N、道路数R和roads数组作为参数,返回从城市1到城市N的最短路径长度。

dijkstra函数中,我们首先初始化距离数组distance和访问标记数组visited。然后,我们设置起点城市1的距离为0。

接下来,我们进行N-1次迭代。在每次迭代中,我们找到距离最小且未被访问的城市,将其标记为已访问,并更新与该城市相邻的城市的距离。我们只更新满足以下条件的城市:未被访问、从当前城市到相邻城市的距离不超过K,并且经过该道路的通行费加上当前城市的距离小于相邻城市的当前距离。

最后,我们返回距离数组中城市N的距离作为最终结果。如果距离数组中城市N的值为INT_MAX,表示不存在满足条件的路径,输出-1;否则,输出该值。

综上所述,这个C语言程序可以根据输入的金币数、城市数和道路信息,计算出从城市1到城市N的最短路径长度,并输出结果。

第2题:Rainbow的商店

Rainbow开了一家商店,在一次进货中获得了N个商品。

已知每个商品的利润和过期时间。

Rainbow每天只能卖一个商品,并且过期商品不能再卖。

Rainbow也可以选择在每天出售哪个商品,并且一定可以卖出。

由于这些限制,Rainbow需要制定一份合理的售卖计划。请你计算一下,Rainbow最终可以获得的最大收益。

时间限制:1000

内存限制:262144
输入

第一行两个整数N。 接下来N行每行两个整数,分别表示每个商品的利润、过期时间。 1<=N,利润,时间<=10000。
输出

输出一个整数,表示Rainbow最终可以获得的最大收益。
样例输入

7

20 1

2 1

10 3

100 2

8 2

5 20

50 10
样例输出

185
提示

第1天卖出20 第2天卖出100 第3天卖出10 第4天卖出50(实际上只要在第10天卖就可以) 第5天卖出5(实际上只要在第20天前卖就可以) 总计185 其它2件商品由于过期、每天只能卖一个的限制,在最优策略下应该不出售。

这个问题可以使用动态规划来解决。我们可以定义一个状态数组dp,其中dp[i]表示在第i天能够获得的最大收益。

以下是一个解决Rainbow商店问题的C语言程序:

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

#define MAX_N 100

int maxProfit(int N, int profits[], int expiration[]) {
    int dp[MAX_N + 1] = {0};
    int i, j;

    for (i = 1; i <= N; i++) {
        int maxProfit = profits[i];  // 假设卖出当前商品
        for (j = 1; j < i; j++) {
            if (i - j <= expiration[j] && dp[j] + profits[i] > maxProfit) {
                maxProfit = dp[j] + profits[i];  // 更新最大收益
            }
        }
        dp[i] = maxProfit;
    }

    return dp[N];
}

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

    int profits[MAX_N];
    int expiration[MAX_N];
    int i;
    for (i = 1; i <= N; i++) {
        scanf("%d %d", &profits[i], &expiration[i]);
    }

    int result = maxProfit(N, profits, expiration);
    printf("%d\n", result);

    return 0;
}

在这个解决方案中,我们首先读取输入的商品数量N。

然后,我们创建两个数组profitsexpiration,分别用于存储商品的利润和过期时间。

接下来,我们使用循环读取每个商品的利润和过期时间,并将它们存储在相应的数组中。

然后,我们调用maxProfit函数计算Rainbow最终可以获得的最大收益。该函数接受商品数量N、利润数组profits和过期时间数组expiration作为参数,返回最大收益。

maxProfit函数中,我们定义一个状态数组dp,并初始化为0。然后,我们进行迭代计算每一天的最大收益。

对于第i天,我们假设卖出当前商品,将其利润加到当前最大收益中。然后,我们遍历第i天之前的每一天j,如果卖出第j天的商品可以在第i天之前卖出,并且卖出第j天的商品加上当前商品的利润可以得到更大的收益,就更新最大收益。

最后,我们返回dp[N]作为最终结果,即Rainbow最终可以获得的最大收益。

综上所述,这个C语言程序可以根据输入的商品利润和过期时间,计算出Rainbow最终可以获得的最大收益,并输出结果。

第3题:冰阔落

老王喜欢喝冰阔落。

初始时刻,桌面上有n杯阔落,编号为1到n。老王总想把其中一杯阔落倒到另一杯中,这样他一次性就能喝很多很多阔落,假设杯子的容量是足够大的。

有m 次操作,每次操作包含两个整数x与y。

若原始编号为x 的阔落与原始编号为y的阔落已经在同一杯,请输出"Yes";否则,我们将原始编号为y 所在杯子的所有阔落,倒往原始编号为x 所在的杯子,并输出"No"。

最后,老王想知道哪些杯子有冰阔落。

时间限制:10000

内存限制:65536
输入

有多组测试数据,少于 5 组。 每组测试数据,第一行两个整数 n, m (n, m<=50000)。接下来 m 行,每行两个整数 x, y (1<=x, y<=n)。
输出

每组测试数据,前 m 行输出 "Yes" 或者 "No"。 第 m+1 行输出一个整数,表示有阔落的杯子数量。 第 m+2 行有若干个整数,从小到大输出这些杯子的编号。
样例输入

3 2

1 2

2 1

4 2

1 2

4 3
样例输出

No

Yes

2

1 3

No

No

2

1 4

这个问题可以使用并查集(Disjoint Set)来解决。我们可以创建一个数组parent,用于记录每个杯子所属的集合。

以下是一个解决"冰阔落 I"问题的C语言程序:

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

#define MAX_N 50000

int parent[MAX_N + 1];  // 并查集数组

int find(int x) {
    if (parent[x] == x) {
        return x;
    }
    return parent[x] = find(parent[x]);  // 路径压缩
}

void merge(int x, int y) {
    int px = find(x);
    int py = find(y);
    if (px != py) {
        parent[py] = px;
    }
}

int main() {
    int n, m;
    while (scanf("%d %d", &n, &m) == 2) {
        int i;
        for (i = 1; i <= n; i++) {
            parent[i] = i;  // 初始化并查集
        }

        for (i = 1; i <= m; i++) {
            int x, y;
            scanf("%d %d", &x, &y);
            if (find(x) == find(y)) {
                printf("Yes\n");
            } else {
                printf("No\n");
                merge(x, y);  // 合并两个杯子所属的集合
            }
        }

        int count = 0;
        printf("%d\n", n);  // 输出杯子数量,初始假设所有杯子都有阔落
        for (i = 1; i <= n; i++) {
            if (parent[i] == i) {
                printf("%d ", i);  // 输出有阔落的杯子编号
                count++;
            }
        }
        printf("\n");
    }

    return 0;
}

在这个解决方案中,我们使用一个循环读取多组测试数据。

对于每组测试数据,我们首先读取杯子的数量n和操作次数m。

然后,我们创建一个大小为n的并查集数组parent,并将每个杯子初始化为属于自己的集合。

接下来,我们进行m次操作。对于每次操作,我们读取两个杯子的编号x和y。

如果x和y已经在同一个集合中(即它们属于同一杯阔落),则输出"Yes"。

如果x和y不在同一个集合中,我们将原始编号为y所在杯子的所有阔落倒入原始编号为x所在的杯子,并输出"No"。然后,我们将这两个杯子所属的集合合并。

完成m次操作后,我们统计有阔落的杯子数量,并输出这些杯子的编号。

综上所述,这个C语言程序可以根据输入的阔落杯子编号和操作,输出每次操作的结果以及最后有阔落的杯子数量和编号。

第4题:青蛙的约会

两只青蛙在网上相识了,它们聊得很开心,于是觉得很有必要见一面。它们很高兴地发现它们住在同一条纬度线上,于是它们约定各自朝西跳,直到碰面为止。可是它们出发之前忘记了一件很重要的事情,既没有问清楚对方的特征,也没有约定见面的具体位置。不过青蛙们都是很乐观的,它们觉得只要一直朝着某个方向跳下去,总能碰到对方的。但是除非这两只青蛙在同一时间跳到同一点上,不然是永远都不可能碰面的。为了帮助这两只乐观的青蛙,你被要求写一个程序来判断这两只青蛙是否能够碰面,会在什么时候碰面。 我们把这两只青蛙分别叫做青蛙A和青蛙B,并且规定纬度线上东经0度处为原点,由东往西为正方向,单位长度1米,这样我们就得到了一条首尾相接的数轴。设青蛙A的出发点坐标是x,青蛙B的出发点坐标是y。青蛙A一次能跳m米,青蛙B一次能跳n米,两只青蛙跳一次所花费的时间相同。纬度线总长L米。现在要你求出它们跳了几次以后才会碰面。

时间限制:1000

内存限制:65536
输入

输入只包括一行5个整数x,y,m,n,L,其中x≠y < 2000000000,0 < m、n < 2000000000,0 < L < 2100000000。
输出

输出碰面所需要的跳跃次数,如果永远不可能碰面则输出一行"Impossible"
样例输入

1 2 3 4 5
样例输出

4

这个问题可以通过求解一元线性同余方程来解决。线性同余方程的一般形式为ax ≡ b (mod m),其中a、b、m为已知整数,x为未知数。在这个问题中,我们需要求解的是 (x + mt) ≡ (y + nt) (mod L),其中t为正整数,表示跳了多少次。

以下是一个解决"青蛙的约会"问题的C语言程序:

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

long long gcd(long long a, long long b) {
    if (b == 0) {
        return a;
    }
    return gcd(b, a % b);
}

int main() {
    long long x, y, m, n, L;
    scanf("%lld %lld %lld %lld %lld", &x, &y, &m, &n, &L);

    long long diff = y - x;
    long long lcm = m * n / gcd(m, n);

    if (diff % gcd(m, n) != 0) {
        printf("Impossible\n");
    } else {
        diff = (diff % L + L) % L;  // 防止diff为负数
        long long ans = diff / gcd(m, n) % (L / gcd(m, n));
        printf("%lld\n", ans);
    }

    return 0;
}

在这个解决方案中,我们首先读取输入的x、y、m、n和L。

我们计算出差值diff = y - x,并计算m和n的最小公倍数lcm = m * n / gcd(m, n)。

如果diff不是gcd(m, n)的倍数,则说明两只青蛙永远不会碰面,输出"Impossible"。

否则,我们将diff取模L,并确保结果为非负数。然后,我们计算答案ans = diff / gcd(m, n) % (L / gcd(m, n))。这里我们将结果除以gcd(m, n),以确保ans是最小非负整数。

最后,我们输出答案ans,表示两只青蛙碰面所需要的跳跃次数。

综上所述,这个C语言程序可以根据输入的参数,计算出两只青蛙碰面所需要的跳跃次数。如果永远不可能碰面,则输出"Impossible"。

相关推荐
嵌入式科普2 分钟前
十三、从0开始卷出一个新项目之瑞萨RZN2L串口DMA接收不定长
c语言·stm32·瑞萨·e2studio·rzn2l
_WndProc3 分钟前
C++ 日志输出
开发语言·c++·算法
薄荷故人_4 分钟前
从零开始的C++之旅——红黑树及其实现
数据结构·c++
m0_748240025 分钟前
Chromium 中chrome.webRequest扩展接口定义c++
网络·c++·chrome
qq_4335545412 分钟前
C++ 面向对象编程:+号运算符重载,左移运算符重载
开发语言·c++
努力学习编程的伍大侠16 分钟前
基础排序算法
数据结构·c++·算法
XiaoLeisj44 分钟前
【递归,搜索与回溯算法 & 综合练习】深入理解暴搜决策树:递归,搜索与回溯算法综合小专题(二)
数据结构·算法·leetcode·决策树·深度优先·剪枝
yuyanjingtao1 小时前
CCF-GESP 等级考试 2023年9月认证C++四级真题解析
c++·青少年编程·gesp·csp-j/s·编程等级考试
Jasmine_llq1 小时前
《 火星人 》
算法·青少年编程·c#