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

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

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

第1题:生理周期

人生来就有三个生理周期,分别为体力、感情和智力周期,它们的周期长度为23天、28天和33天。每一个周期中有一天是高峰。在高峰这天,人会在相应的方面表现出色。例如,智力周期的高峰,人会思维敏捷,精力容易高度集中。因为三个周期的周长不同,所以通常三个周期的高峰不会落在同一天。对于每个人,我们想知道何时三个高峰落在同一天。对于每个周期,我们会给出从当前年份的第一天开始,到出现高峰的天数(不一定是第一次高峰出现的时间)。你的任务是给定一个从当年第一天开始数的天数,输出从给定时间开始(不包括给定时间)下一次三个高峰落在同一天的时间(距给定时间的天数)。例如:给定时间为10,下次出现三个高峰同天的时间是12,则输出2(注意这里不是3)。
时间限制:1000
内存限制:65536
输入
一行,包含四个整数:p, e, i和d,相邻两个整数之间用单个空格隔开。 p, e, i分别表示体力、情感和智力高峰出现的时间(时间从当年的第一天开始计算)。d 是给定的时间,可能小于p, e, 或 i。 所有给定时间是非负的并且小于等于365, 所求的时间小于等于21252。
输出
一个整数,即从给定时间起,下一次三个高峰同天的时间(距离给定时间的天数)。
样例输入
4 5 6 7
样例输出
16994

以下是一个解决生理周期问题的C语言程序:

#include 

int main() {
    int p, e, i, d;
    scanf("%d %d %d %d", &p, &e, &i, &d);

    int caseNum = 1;
    while (1) {
        int days = caseNum * 21252 + d;

        if ((days - p) % 23 == 0 && (days - e) % 28 == 0 && (days - i) % 33 == 0) {
            printf("%d\n", caseNum * 21252);
            break;
        }

        caseNum++;
    }

    return 0;
}

在这个解决方案中,我们首先读取输入的体力、感情和智力高峰出现的时间p、e和i,以及给定的时间d。

然后,我们使用一个循环来逐个尝试不同的案例,直到找到一个满足三个高峰同天的时间。

在每个案例中,我们计算出总天数days,通过caseNum * 21252 + d计算得到,其中21252是三个周期的最小公倍数。

接下来,我们检查是否满足三个高峰同天的条件,即(days - p) % 23 == 0(days - e) % 28 == 0(days - i) % 33 == 0。如果满足条件,则输出距离给定时间的天数caseNum * 21252,并结束循环。

如果不满足条件,我们增加案例的数量caseNum,并继续下一个循环。

综上所述,这个C语言程序可以根据给定的体力、感情和智力高峰出现的时间,以及给定的时间,计算出从给定时间起,下一次三个高峰同天的时间(距离给定时间的天数)。

第2题:开关问题

有N个相同的开关,每个开关都与某些开关有着联系,每当你打开或者关闭某个开关的时候,其他的与此开关相关联的开关也会相应地发生变化,即这些相联系的开关的状态如果原来为开就变为关,如果为关就变为开。你的目标是经过若干次开关操作后使得最后N个开关达到一个特定的状态。对于任意一个开关,最多只能进行一次开关操作。你的任务是,计算有多少种可以达到指定状态的方法。(不计开关操作的顺序)
时间限制:1000
内存限制:65536
输入
输入第一行有一个数K,表示以下有K组测试数据。 每组测试数据的格式如下: 第一行 一个数N(0 < N < 29) 第二行 N个0或者1的数,表示开始时N个开关状态。 第三行 N个0或者1的数,表示操作结束后N个开关的状态。 接下来 每行两个数I J,表示如果操作第 I 个开关,第J个开关的状态也会变化。每组数据以 0 0 结束。
输出
如果有可行方法,输出总数,否则输出“Oh,it’s impossible~!!” 不包括引号
样例输入
2
3
0 0 0
1 1 1
1 2
1 3
2 1
2 3
3 1
3 2
0 0
3
0 0 0
1 0 1
1 2
2 1
0 0
样例输出
4
Oh,it’s impossible~!!
提示
第一组数据的说明:一共以下四种方法: 操作开关1 操作开关2 操作开关3 操作开关1、2、3 (不记顺序)

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

#include 
#include 

int N;
int initialStatus[29];
int targetStatus[29];
int connections[29][29];

int count = 0;

void dfs(int switchIndex) {
    if (switchIndex > N) {
        int i;
        for (i = 1; i <= N; i++) {
            if (initialStatus[i] != targetStatus[i]) {
                return;
            }
        }
        count++;
        return;
    }

    initialStatus[switchIndex] = 1 - initialStatus[switchIndex];
    int i;
    for (i = 1; i <= N; i++) {
        if (connections[switchIndex][i] == 1) {
            initialStatus[i] = 1 - initialStatus[i];
        }
    }

    dfs(switchIndex + 1);

    initialStatus[switchIndex] = 1 - initialStatus[switchIndex];
    for (i = 1; i <= N; i++) {
        if (connections[switchIndex][i] == 1) {
            initialStatus[i] = 1 - initialStatus[i];
        }
    }

    dfs(switchIndex + 1);
}

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

    while (T--) {
        scanf("%d", &N);

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

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

        for (i = 1; i <= N; i++) {
            for (j = 1; j <= N; j++) {
                connections[i][j] = 0;
            }
        }

        int I, J;
        while (scanf("%d %d", &I, &J) == 2 && (I != 0 || J != 0)) {
            connections[I][J] = 1;
        }

        count = 0;
        dfs(1);

        if (count > 0) {
            printf("%d\n", count);
        } else {
            printf("Oh,it's impossible~!!\n");
        }
    }

    return 0;
}

在这个解决方案中,我们首先定义了一个全局变量N来表示开关的数量,以及一些全局数组来存储开关的初始状态、目标状态和开关之间的联系。

然后,我们定义了一个递归函数dfs来尝试所有可能的开关操作。在dfs函数中,我们首先判断当前开关状态是否与目标状态相同,如果相同则将计数器count加1。

接下来,我们尝试操作当前开关,并更新与之相关联的开关状态。然后,递归调用dfs函数来继续处理下一个开关。

在递归调用之后,我们需要恢复当前开关和相关联开关的状态,以便尝试其他可能的操作。

在主函数中,我们首先读取输入的测试数据组数T。

然后,对于每组测试数据,我们读取开关的数量N,初始状态和目标状态。

接下来,我们读取开关之间的联系,并将其存储在connections数组中。

然后,我们调用dfs函数来尝试所有可能的开关操作,并根据计数器count的值输出结果。

综上所述,这个C语言程序可以根据输入的开关状态和联系,计算出达到指定状态的方法总数,或者判断无法达到指定状态。

第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

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

#include 

int parent[50001];
int count[50001];

int find(int x) {
    if (parent[x] != x) {
        parent[x] = find(parent[x]);
    }
    return parent[x];
}

void merge(int x, int y) {
    int rootX = find(x);
    int rootY = find(y);

    if (rootX != rootY) {
        parent[rootY] = rootX;
        count[rootX] += count[rootY];
    }
}

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

    while (T--) {
        int n, m;
        scanf("%d %d", &n, &m);

        int i;
        for (i = 1; i <= n; i++) {
            parent[i] = i;
            count[i] = 1;
        }

        for (i = 0; i < m; i++) {
            int x, y;
            scanf("%d %d", &x, &y);

            int rootX = find(x);
            int rootY = find(y);

            if (rootX == rootY) {
                printf("Yes\n");
            } else {
                printf("No\n");
                merge(rootX, rootY);
            }
        }

        int totalCups = 0;
        for (i = 1; i <= n; i++) {
            if (parent[i] == i) {
                totalCups++;
            }
        }

        printf("%d\n", totalCups);

        for (i = 1; i <= n; i++) {
            if (parent[i] == i) {
                printf("%d ", i);
            }
        }
        printf("\n");
    }

    return 0;
}

在这个解决方案中,我们使用并查集数据结构来处理冰阔落的问题。

首先,我们定义了两个数组parentcount,作为并查集的辅助数组。parent数组用于记录每个杯子的父节点,count数组用于记录每个集合(杯子所在的连通分量)的杯子数量。

在主函数中,我们首先读取输入的测试数据组数T。

然后,对于每组测试数据,我们读取阔落杯子的数量n和操作次数m,并初始化并查集数组。

接下来,我们循环读取每次操作的两个杯子编号x和y。

对于每次操作,我们首先判断两个杯子是否在同一个连通分量(集合)中,即它们的根节点是否相同。如果根节点相同,则输出"Yes"。

如果根节点不同,则输出"No",并将两个集合进行合并,即将其中一个集合的根节点的父节点设置为另一个集合的根节点,并更新合并后集合的杯子数量。

完成所有操作后,我们统计并输出有阔落的杯子数量。遍历数组parent,找到根节点为自身的杯子,即代表一个集合的根节点,输出它们的编号。

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

第4题:最短路

给定一个n个点, m条边的有向图, 求从点S出发, 到其它所有点的最短路径.
时间限制:2000
内存限制:65536
输入
第一行一个整数T, 表示有T组数据 对于每组测试数据, 第一行三个整数n, m, S, 表示有n个点, m条边, 起点为S. 接下来m行, 每行三个整数x, y, z, 代表从x到y有长度为z的边 点的编号从1到n T <= 10, n <= 10000, m <= 20000, |z| <= 10000. 所有数据的n之和 <= 30000, 所有数据的m之和 <= 60000.
输出
对于每组数据: 如果从S点出发可以走入负圈 (即到某些点的最短路径可以无限小), 那么输出一行Error. 否则, 输出一行用空格分隔的n个整数, 其中第i个整数表示从S点到i点的最短路长度. 如果从S点无法到达i点, 则第i个输出为”null”.
样例输入
4
5 7 1
1 2 3
2 3 4
3 4 8
1 3 9
4 5 1
1 4 5
1 5 10
4 4 1
1 2 -4
2 3 8
1 3 5
3 4 0
3 3 2
1 2 -3
2 3 -4
3 1 6
4 2 1
1 2 1
3 4 2
样例输出
0 3 7 5 6
0 -4 4 4
Error
0 1 null null

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

#include 
#include 
#include 

#define MAXN 10001
#define MAXM 20001

typedef struct {
    int u, v, w;
} Edge;

int dist[MAXN];
Edge edges[MAXM];

void BellmanFord(int n, int m, int s) {
    int i, j;

    // 初始化距离数组
    for (i = 1; i <= n; i++) {
        dist[i] = INT_MAX;
    }
    dist[s] = 0;

    // 进行n-1轮松弛操作
    for (i = 1; i <= n - 1; i++) {
        for (j = 0; j < m; j++) {
            int u = edges[j].u;
            int v = edges[j].v;
            int w = edges[j].w;

            if (dist[u] != INT_MAX && dist[u] + w < dist[v]) {
                dist[v] = dist[u] + w;
            }
        }
    }

    // 检查是否存在负圈
    bool hasNegativeCycle = false;
    for (j = 0; j < m; j++) {
        int u = edges[j].u;
        int v = edges[j].v;
        int w = edges[j].w;

        if (dist[u] != INT_MAX && dist[u] + w < dist[v]) {
            hasNegativeCycle = true;
            break;
        }
    }

    // 输出结果
    if (hasNegativeCycle) {
        printf("Error\n");
    } else {
        for (i = 1; i <= n; i++) {
            if (dist[i] != INT_MAX) {
                printf("%d ", dist[i]);
            } else {
                printf("null ");
            }
        }
        printf("\n");
    }
}

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

    while (T--) {
        int n, m, s;
        scanf("%d %d %d", &n, &m, &s);

        int i;
        for (i = 0; i < m; i++) {
            scanf("%d %d %d", &edges[i].u, &edges[i].v, &edges[i].w);
        }

        BellmanFord(n, m, s);
    }

    return 0;
}

在这个解决方案中,我们使用了Bellman-Ford算法来求解最短路问题。

首先,我们定义了一个结构体Edge来表示图中的边,包含起点、终点和边的权重。

然后,我们定义了一个距离数组dist用于存储从起点到各个点的最短路长度。

BellmanFord函数中,我们首先对距离数组进行初始化,将起点的距离设置为0,其余点的距离设置为无穷大。

然后,我们进行n-1轮松弛操作,遍历所有边,根据边的权重更新起点到终点的距离,如果找到了更短的路径,则更新距离数组。

接下来,我们检查是否存在负圈,即进行第n轮松弛操作,如果仍然能够找到更短的路径,则说明存在负圈。

最后,根据是否存在负圈来输出结果。如果存在负圈,则输出"Error";否则,输出从起点到各个点的最短路长度。

在主函数中,我们首先读取输入的测试数据组数T。

然后,对于每组测试数据,我们读取点的数量n、边的数量m和起点S,以及m行描述边的信息。

最后,我们调用BellmanFord函数来求解最短路问题,并输出结果。

综上所述,这个C语言程序可以根据输入的有向图和起点,求解从起点到其它所有点的最短路径,并输出结果。如果存在负圈,则输出"Error"。

你可能感兴趣的:(c语言,c++,等级考试,电子学会)