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

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

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

以下是一个求解最短路径问题的C语言解决方案:

#include 
#include 
#include 
#include 

#define MAX_CITIES 100
#define MAX_ROADS 100

typedef struct {
    int destination;
    int length;
    int toll;
} Road;

typedef struct {
    int city;
    int cost;
} Node;

int dijkstra(int start, int end, int max_cost, int num_cities, Road roads[]) {
    int distances[MAX_CITIES + 1];
    bool visited[MAX_CITIES + 1];

    for (int i = 1; i <= num_cities; i++) {
        distances[i] = INT_MAX;
        visited[i] = false;
    }

    distances[start] = 0;

    while (true) {
        int min_distance = INT_MAX;
        int current_city = -1;

        for (int i = 1; i <= num_cities; i++) {
            if (!visited[i] && distances[i] < min_distance) {
                min_distance = distances[i];
                current_city = i;
            }
        }

        if (current_city == -1 || current_city == end) {
            break;
        }

        visited[current_city] = true;

        for (int i = 0; i < num_cities; i++) {
            Road road = roads[i];
            if (road.destination == current_city && road.toll <= max_cost) {
                int new_distance = distances[current_city] + road.length;
                if (new_distance < distances[road.destination]) {
                    distances[road.destination] = new_distance;
                }
            }
        }
    }

    return distances[end] == INT_MAX ? -1 : distances[end];
}

int main() {
    int max_cost, num_cities, num_roads;
    scanf("%d %d %d", &max_cost, &num_cities, &num_roads);

    Road roads[MAX_ROADS];

    for (int i = 0; i < num_roads; i++) {
        int start, end, length, toll;
        scanf("%d %d %d %d", &start, &end, &length, &toll);
        Road road = {end, length, toll};
        roads[i] = road;
    }

    int shortest_distance = dijkstra(1, num_cities, max_cost, num_cities, roads);

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

    return 0;
}

在这个解决方案中,我们使用Dijkstra算法来求解最短路径。首先定义了两个结构体RoadNodeRoad结构体表示一条道路,包含目的地城市、长度和通行费。Node结构体表示Dijkstra算法中的节点,包含城市和到达该城市的花费。

然后,我们实现了dijkstra函数来执行Dijkstra算法。该函数接收起始城市、终点城市、最大金币数、城市数量和道路数组作为参数。在函数中,我们使用distances数组来记录起始城市到每个城市的最短距离,并使用visited数组来标记已访问的城市。

在主函数中,我们根据输入读取最大金币数、城市数量和道路数量,并读取每条道路的起始城市、终点城市、长度和通行费。然后,我们调用dijkstra函数,并输出最短路径的长度。

需要注意的是,如果最短路径不存在,则输出-1。

综上所述,这个C语言解决方案可以帮助Bob找到从城市1到城市N的最短路径,并确保路径上的花费不超过他能够承受的金币数。

第2题:控制公司

有些公司是其他公司的部分拥有者,因为他们获得了其他公司发行的股票的一部分。例如,福特公司拥有马自达公司12%的股票。据说,如果至少满足了以下条件之一,公司A就可以控制公司B了:
l 公司A = 公司B。
l 公司A拥有大于50%的公司B的股票。
l 公司A控制K(K >= 1)个公司,记为C1, …, CK,每个公司Ci拥有xi%的公司B的股票,并且x1+ … + xK > 50%。(ps:A可以控制自己,即Ci可以为A)
你将被给予一系列的三对数(i,j,p),表明公司i拥有公司j的p%的股票。计算所有的数对(h,s),表明公司h控制公司s。
写一个程序读入三对数(i,j,p),并且找出所有的数对(h,s),使得公司h控制公司s。
时间限制:1000
内存限制:65536
输入
第一行: N,表明接下来三对数的数量。 第二行到第N+1行:每行三个整数作为一个三对数(i,j,p),如上文所述。 I,J≤100,N,P≤100
输出
输出零个或更多个的控制其他公司的公司。每行包括两个整数表明序号为第一个整数的公司控制了序号为第二个整数的公司。将输出的每行以第一个数字升序排列(并且第二个数字也升序排列来避免并列)。请不要输出控制自己的公司。
样例输入
3
1 2 80
2 3 80
3 1 20
样例输出
1 2
1 3
2 3

以下是一个解决控制公司问题的C语言程序:

#include 
#include 

#define MAX_COMPANIES 100

bool isControlled[MAX_COMPANIES + 1][MAX_COMPANIES + 1];

void updateControl(int i, int j, int p, int num_companies) {
    if (p >= 50) {
        isControlled[i][j] = true;
    }

    for (int k = 1; k <= num_companies; k++) {
        if (isControlled[j][k]) {
            isControlled[i][k] = true;
        }
    }
}

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

    int i, j, p;
    int num_companies = 0;

    for (int k = 0; k < num_pairs; k++) {
        scanf("%d %d %d", &i, &j, &p);
        updateControl(i, j, p, num_companies);
        num_companies = (i > num_companies) ? i : num_companies;
        num_companies = (j > num_companies) ? j : num_companies;
    }

    for (int h = 1; h <= num_companies; h++) {
        for (int s = 1; s <= num_companies; s++) {
            if (h != s && isControlled[h][s]) {
                printf("%d %d\n", h, s);
            }
        }
    }

    return 0;
}

在这个解决方案中,我们使用一个布尔型的二维数组isControlled来记录公司之间的控制关系。数组的行表示控制的公司,列表示被控制的公司。如果isControlled[i][j]true,则表示公司i控制公司j。

我们定义了一个updateControl函数来更新控制关系。该函数接收三个参数:i、j和p,表示公司i拥有公司j的p%的股票。函数根据控制条件更新isControlled数组。

在主函数中,我们首先读取三对数的数量,并定义了变量num_companies来记录公司的数量。

然后,我们使用一个循环读取每个三对数,并调用updateControl函数来更新控制关系。同时,我们通过比较i和j的大小来更新num_companies的值,确保其为最大公司的编号。

最后,我们遍历isControlled数组,输出所有控制其他公司的公司的编号对。

需要注意的是,我们不输出控制自己的公司。

综上所述,这个C语言程序可以帮助找出所有控制其他公司的公司,并输出它们的编号对。

第3题:发现它,抓住它

一个城市中有两个犯罪团伙A和B,你需要帮助警察判断任意两起案件是否是同一个犯罪团伙所为,警察所获得的信息是有限的。假设现在有N起案件(N<=100000),编号为1到N,每起案件由团伙A或团伙B所为。你将按时间顺序获得M条信息(M<=100000),这些信息分为两类:
D [a] [b]
其中[a]和[b]表示两起案件的编号,这条信息表明它们属于不同的团伙所为
A [a] [b]
其中[a]和[b]表示两起案件的编号,这条信息需要你回答[a]和[b]是否是同一个团伙所为
注意你获得信息的时间是有先后顺序的,在回答的时候只能根据已经接收到的信息做出判断。
时间限制:1000
内存限制:65536
输入
第一行是测试数据的数量T(1<=T<=20)。 每组测试数据的第一行包括两个数N和M,分别表示案件的数量和信息的数量,其后M行表示按时间顺序收到的M条信息。
输出
对于每条需要回答的信息,你需要输出一行答案。如果是同一个团伙所为,回答"In the same gang.“,如果不是,回答"In different gangs.”,如果不确定,回答”Not sure yet."。
样例输入
1
5 5
A 1 2
D 1 2
A 1 2
D 2 4
A 1 4
样例输出
Not sure yet.
In different gangs.
In the same gang.

以下是一个解决犯罪团伙问题的C语言程序:

#include 
#include 

#define MAX_CASES 100000

int parent[MAX_CASES + 1];

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

void unionSets(int x, int y) {
    int parentX = find(x);
    int parentY = find(y);
    parent[parentX] = parentY;
}

bool areSameGang(int x, int y) {
    return find(x) == find(y);
}

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

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

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

        char type;
        int a, b;

        while (M--) {
            scanf(" %c %d %d", &type, &a, &b);

            if (type == 'A') {
                if (areSameGang(a, b)) {
                    printf("In the same gang.\n");
                } else {
                    printf("In different gangs.\n");
                }
            } else if (type == 'D') {
                unionSets(a, b);
            }
        }
    }

    return 0;
}

在这个解决方案中,我们使用并查集来判断案件是否属于同一个犯罪团伙。我们使用一个数组parent来记录每个案件所属的团伙,初始时每个案件的团伙编号为其自身。

我们定义了find函数来查找案件所属的团伙编号。如果案件的团伙编号与其自身相同,则表示找到了根节点,即最终的团伙编号。否则,递归调用find函数继续查找。

我们还定义了unionSets函数来合并两个团伙。该函数接收两个案件的编号,找到它们所属的团伙编号,并将其中一个团伙的根节点的父节点设置为另一个团伙的根节点,实现合并。

在主函数中,我们首先读取测试数据的数量T,并使用一个循环处理每组测试数据。

对于每组测试数据,我们首先读取案件的数量N和信息的数量M,并初始化parent数组。

然后,我们使用一个循环读取每条信息,并根据信息的类型进行相应的操作。如果是"A"类型的信息,我们调用areSameGang函数判断两起案件是否属于同一个团伙,并输出相应的答案。如果是"D"类型的信息,我们调用unionSets函数合并两个团伙。

综上所述,这个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

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

#include 

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

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

    unsigned long long distance = (y - x + L) % L;
    unsigned long long commonJump = gcd(m, n);

    if (distance % commonJump != 0) {
        printf("Impossible\n");
    } else {
        unsigned long long jumps = (L / commonJump) - (distance / commonJump);
        printf("%llu\n", jumps);
    }

    return 0;
}

在这个解决方案中,我们首先定义了一个函数gcd来计算两个数的最大公约数,采用辗转相除法。

在主函数中,我们首先读取输入的青蛙的初始位置x和y,每次跳跃的距离m和n,以及纬度线的总长度L。

然后,我们计算出距离distance,即青蛙B相对于青蛙A的位置。由于纬度线是首尾相接的数轴,我们使用(y - x + L) % L来确保距离始终为正数。

接下来,我们计算出两只青蛙能够同时到达的最小公倍数commonJump。这个公倍数表示两只青蛙需要跳多少次才能同时到达同一位置。

如果distance不能整除commonJump,说明两只青蛙永远不可能碰面,输出"Impossible"。

否则,我们计算出需要的跳跃次数jumps,通过(L / commonJump) - (distance / commonJump)计算得到。其中,(L / commonJump)表示纬度线上的周期数,(distance / commonJump)表示已经跳过的周期数。

最后,我们输出计算得到的跳跃次数jumps。

综上所述,这个C语言程序可以根据输入的青蛙的初始位置和跳跃距离,计算出它们跳了几次以后才会碰面,或者判断永远不可能碰面。

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