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

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

第1题:公共子序列

我们称序列Z = < z1, z2, …, zk >是序列X = < x1, x2, …, xm >的子序列当且仅当存在 严格上升 的序列< i1, i2, …, ik >,使得对j = 1, 2, … ,k, 有xij = zj。比如Z = < a, b, f, c > 是X = < a, b, c, f, b, c >的子序列。 现在给出两个序列X和Y,你的任务是找到X和Y的最大公共子序列,也就是说要找到一个最长的序列Z,使得Z既是X的子序列也是Y的子序列。
时间限制:3000
内存限制:65536
输入
输入包括多组测试数据。每组数据包括一行,给出两个长度不超过200的字符串,表示两个序列。两个字符串之间由若干个空格隔开。
输出
对每组输入数据,输出一行,给出两个序列的最大公共子序列的长度。
样例输入
abcfbc abfcab
programming contest
abcd mnp
样例输出
4
2
0

要解决最大公共子序列问题,可以使用动态规划的方法。

以下是使用C语言实现的代码:

#include 
#include 

#define MAX_LENGTH 200

int max(int a, int b) {
    return (a > b) ? a : b;
}

int longestCommonSubsequence(char X[], char Y[], int m, int n) {
    int dp[MAX_LENGTH + 1][MAX_LENGTH + 1];

    for (int i = 0; i <= m; i++) {
        for (int j = 0; j <= n; j++) {
            if (i == 0 || j == 0) {
                dp[i][j] = 0;
            } else if (X[i - 1] == Y[j - 1]) {
                dp[i][j] = dp[i - 1][j - 1] + 1;
            } else {
                dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
            }
        }
    }

    return dp[m][n];
}

int main() {
    char X[MAX_LENGTH + 1];
    char Y[MAX_LENGTH + 1];

    while (scanf("%s %s", X, Y) != EOF) {
        int m = strlen(X);
        int n = strlen(Y);

        int length = longestCommonSubsequence(X, Y, m, n);
        printf("%d\n", length);
    }

    return 0;
}

该代码使用一个二维数组dp来保存状态,其中dp[i][j]表示序列X的前i个字符和序列Y的前j个字符的最大公共子序列的长度。

首先,将dp[i][0]和dp[0][j]都设置为0,表示当一个序列的长度为0时,最大公共子序列的长度为0。

然后,从1到m和1到n的循环遍历,如果X[i-1]等于Y[j-1],则说明X的第i个字符和Y的第j个字符相同,将dp[i][j]设置为dp[i-1][j-1]的值加1,表示当前字符可以加入最大公共子序列。

如果X[i-1]不等于Y[j-1],则说明X的第i个字符和Y的第j个字符不相同,需要在X的前i-1个字符和Y的前j个字符的最大公共子序列和X的前i个字符和Y的前j-1个字符的最大公共子序列之间取最大值,即dp[i-1][j]和dp[i][j-1]的最大值。

最后,dp[m][n]即为X和Y的最大公共子序列的长度。

第2题:硬币问题

有N(不大于100)种硬币,编号为1至N,已知每种硬币的重量(不超过100的正整数)和面额(不超过100的正整数),每种硬币数量不限。选取总重量不超过C(不大于1000的正整数)的硬币,最多能获得多少总面额?
时间限制:1000
内存限制:65536
输入
第一行输入N 第二行输入C 第三行输入各硬币重量,用空格隔开 第四行输入各硬币价值,用空格隔开
输出
最大总面额
样例输入
3
5
1 2 5
1 3 6
样例输出
7

要解决硬币问题,可以使用动态规划的方法。

以下是使用C语言实现的代码:

#include 
#include 

#define MAX_COINS 100
#define MAX_WEIGHT 1000

int max(int a, int b) {
    return (a > b) ? a : b;
}

int maxTotalValue(int coins[], int values[], int n, int capacity) {
    int dp[MAX_WEIGHT + 1];
    memset(dp, 0, sizeof(dp));

    for (int i = 0; i < n; i++) {
        for (int j = coins[i]; j <= capacity; j++) {
            dp[j] = max(dp[j], dp[j - coins[i]] + values[i]);
        }
    }

    return dp[capacity];
}

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

    int capacity;
    scanf("%d", &capacity);

    int coins[MAX_COINS];
    int values[MAX_COINS];

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

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

    int maxTotal = maxTotalValue(coins, values, n, capacity);
    printf("%d\n", maxTotal);

    return 0;
}

该代码使用一个一维数组dp来保存状态,其中dp[j]表示总重量为j时的最大总面额。

首先,将dp数组初始化为0。

然后,从第一种硬币到第N种硬币的循环遍历,对于每种硬币,从其重量coins[i]开始,到总重量C为止的范围内,更新dp[j]的值。更新方式为取dp[j]和dp[j - coins[i]] + values[i]的最大值,表示在总重量为j时,可以选择不放入当前硬币(即dp[j]的值)或放入当前硬币(总重量减去当前硬币重量的最大总面额,再加上当前硬币的面额)。

最后,dp[capacity]即为总重量不超过C时的最大总面额。

第3题:田忌赛马

你一定听过田忌赛马的故事吧?
如果3匹马变成1000匹,齐王仍然让他的马按从优到劣的顺序出赛,田忌可以按任意顺序选择他的赛马出赛。赢一局,田忌可以得到200两银子,输一局,田忌就要输掉200两银子,平局的话不输不赢。
请问田忌最多能赢多少银子?
时间限制:5000
内存限制:65536
输入
输入包含多组测试数据. 每组测试数据的第一行是一个整数n(1<=n<=1000),表示田忌和齐王都拥有n匹马。接下来一行是n个整数,表示田忌的马的速度,下一行也是n个整数,表示齐王的马的速度。 输入的最后以一个0表示结束。
输出
对每组数据,输出一个整数,表示田忌至多可以赢多少银子,如果田忌赢不了,就输出一个负数,表示田忌最少要输多少银子。
样例输入
3
92 83 71
95 87 74
2
20 20
20 20
2
20 19
22 18
0
样例输出
200
0
0

要解决田忌赛马问题,可以使用贪心算法的思想。

以下是使用C语言实现的代码:

#include 
#include 

int compare(const void *a, const void *b) {
    return *(int *)b - *(int *)a;
}

int calculateMaximumSilver(int tianjiHorses[], int qiwangHorses[], int n) {
    int maxSilver = 0;
    qsort(tianjiHorses, n, sizeof(int), compare);
    qsort(qiwangHorses, n, sizeof(int), compare);

    int tianjiIndex = 0;
    int qiwangIndex = 0;

    while (tianjiIndex < n && qiwangIndex < n) {
        if (tianjiHorses[tianjiIndex] > qiwangHorses[qiwangIndex]) {
            maxSilver += 200;
            tianjiIndex++;
            qiwangIndex++;
        } else if (tianjiHorses[tianjiIndex] < qiwangHorses[qiwangIndex]) {
            tianjiIndex++;
        } else {
            qiwangIndex++;
        }
    }

    if (qiwangIndex == n) {
        return maxSilver - 200 * (n - tianjiIndex);
    } else {
        return -maxSilver;
    }
}

int main() {
    int n;
    while (scanf("%d", &n) == 1 && n != 0) {
        int tianjiHorses[1000];
        int qiwangHorses[1000];

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

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

        int maxSilver = calculateMaximumSilver(tianjiHorses, qiwangHorses, n);
        printf("%d\n", maxSilver);
    }

    return 0;
}

该代码首先使用qsort函数对田忌和齐王的马的速度进行降序排序。

然后,使用两个指针tianjiIndexqiwangIndex来遍历田忌和齐王的马的速度数组。

在每一轮比赛中,如果田忌的马速度大于齐王的马速度,则田忌赢得这局比赛,将最大银子数增加200,并将两个指针都向后移动一位。

如果田忌的马速度小于齐王的马速度,则田忌输掉这局比赛,将田忌的指针向后移动一位。

如果田忌的马速度等于齐王的马速度,则平局,将两个指针都向后移动一位。

最后,如果田忌的指针tianjiIndex达到了数组长度n,说明田忌赢得了所有比赛,返回最大银子数;如果齐王的指针qiwangIndex达到了数组长度n,说明田忌输掉了所有比赛,返回负的最大银子数。

第4题:上机

又到周末了,同学们陆陆续续开开心心的来到机房上机。jbr也不例外,但是他到的有点晚,发现有些机位上已经有同学正在做题,有些机位还空着。细心的jbr发现,一位同学来到机房,坐在机位i上,如果他的左右两边都空着,他将获得能力值a[i];如果当他坐下时,左边或者右边已经有一个人在上机了,他将获得能力值b[i];如果当他坐下时,他的左边右边都有人在上机,他将获得能力值c[i]。
同时他发现,已经在上机的同学不会受到刚要坐下的同学的影响,即他们的能力值只会在坐下时产生,以后不会发生变化;第一个机位左边没有机位,最后一个机位右边没有机位,无论何时坐在这两个机位上将无法获得c值。
这时jbr发现有一排机器还空着,一共有N个机位,编号1到N。这时有N位同学们陆陆续续来到机房,一个一个按照顺序坐在这排机位上。聪明的jbr想知道怎么安排座位的顺序,可以使这N位同学获得能力值的和最大呢?
时间限制:1000
内存限制:65536
输入
第一行一个整数N(1<= N <= 10000)
第二行N个数,表示a[i]
第三行N个数,表示b[i]
第四行N个数,表示c[i]
(1<= a[i],b[i],c[i] <=10000)
输出
一个整数,表示获得最大的能力值和
样例输入
4
1 2 2 4
4 3 3 1
2 1 1 2
样例输出
14
提示
第一位同学坐在第四个机位上,获得能力值4; 第二位同学坐在第三个机位上,获得能力值3; 第三位同学坐在第二个机位上,获得能力值3; 第四位同学坐在第一个机位上,获得能力值4; 总和为14。

要解决座位安排的问题,可以使用动态规划的方法。

以下是使用C语言实现的代码:

#include 
#include 

int max(int a, int b) {
    return (a > b) ? a : b;
}

int calculateMaxAbility(int a[], int b[], int c[], int n) {
    int dp[3][10001] = {0};

    dp[0][1] = a[1];
    dp[1][1] = b[1];
    dp[2][1] = 0;

    for (int i = 2; i <= n; i++) {
        dp[0][i] = max(dp[1][i - 1], dp[2][i - 1]) + a[i];
        dp[1][i] = max(dp[0][i - 1], dp[2][i - 1]) + b[i];
        dp[2][i] = max(dp[0][i - 1], dp[1][i - 1]) + c[i];
    }

    return max(max(dp[0][n], dp[1][n]), dp[2][n]);
}

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

    int a[10001];
    int b[10001];
    int c[10001];

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

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

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

    int maxAbility = calculateMaxAbility(a, b, c, n);
    printf("%d\n", maxAbility);

    return 0;
}

该代码使用一个二维数组dp来记录每个位置上的能力值。dp[i][j]表示第j个机位上的同学选择第i种能力值时的最大能力值和。

初始化时,将第一个机位上的能力值分别赋值给dp[0][1]dp[1][1]dp[2][1]

然后,从第二个机位开始,利用动态规划的思想,根据题目给出的能力值计算出每个位置上三种情况的最大能力值和。

最后,返回dp数组中最后一列的最大值,即为获得最大的能力值和。

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