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

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

第1题:漫漫回国路

2020年5月,国际航班机票难求。一位在美国华盛顿的中国留学生,因为一些原因必须在本周内回到北京。现在已知各个机场之间的航班情况,求问他回不回得来(不考虑转机次数和机票价格)。
时间限制:1000
内存限制:65536
输入
第一行为case个数n(n < 10)。 每一个case,第一行为机场个数N,N ≤ 10。 之后的N行,每一行包含N个整数。第i(1 ≤ i ≤ N)行的第j(1 ≤ j ≤ N)个整数代表从第i个机场出发到第j个机场的能买到的航班的最低票价t(0 < t < 10000)。如果不幸没有航班,那么用-1表示。第i行第i个整数为0。 起点华盛顿杜勒斯国际机场的编号为1,终点北京首都国际机场的编号为N。
输出
每一个case一行。 能够回国,输出字符串:YES。如果无法回国,输出字符串:NO
样例输入
2
3
0 100 -1
-1 0 200
-1 -1 0
4
0 1 5 -1
3 0 1 -1
2 4 0 -1
4 1 1 0
样例输出
YES
NO

下面是使用搜索剪枝技术(深度优先搜索)实现的代码示例:

#include 
#include 

#define MAX_N 10
#define INF 10000

int airports[MAX_N][MAX_N];  // 存储机场之间的航班票价
bool visited[MAX_N];  // 记录机场是否被访问过

int n;  // 机场个数
bool found;  // 是否找到回国路径

void dfs(int start, int end) {
    if (start == end) {
        found = true;  // 找到回国路径
        return;
    }

    visited[start] = true;

    for (int i = 1; i <= n; i++) {
        if (!visited[i] && airports[start][i] != -1) {
            dfs(i, end);
        }
    }
}

bool canReturn() {
    found = false;

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

    dfs(1, n);

    return found;
}

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

    while (cases--) {
        scanf("%d", &n);

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

        if (canReturn()) {
            printf("YES\n");
        } else {
            printf("NO\n");
        }
    }

    return 0;
}

这段代码使用深度优先搜索(DFS)算法来判断是否存在一条路径从起点到终点,即从华盛顿到北京。通过维护一个布尔数组 visited 来记录机场是否被访问过,初始时都置为 false。搜索过程中,从起点开始逐个尝试访问每个机场,如果当前机场未被访问且存在航班,则递归地访问下一个机场。如果到达终点,则设置 foundtrue,表示找到回国路径。最终根据 found 的值判断是否能够回国。

请注意,上述代码中使用了一些辅助变量和数据结构(如 foundvisited 数组),这些需要根据实际情况进行定义和实现。

这段代码实现了搜索剪枝技术,并根据题目描述的输入和输出格式进行了相应的处理。你可以将题目给出的示例输入复制到代码中进行测试。

第2题:通配符匹配

给定一个字符串s和一个字符模式p,请实现一个支持’?‘和’*'的通配符匹配功能。
其中‘?’可以匹配任何单个字符,如‘a?c’可以成功匹配‘aac’,‘abc’等字符串,但不可匹配‘ac’,‘aaac’等字符串 。
‘*’ 可以匹配任意长度字符串(包括空字符串),如‘a*c’可以成功匹配‘ac’,‘abdc’,‘abc’,‘aaac’等字符串,但不可匹配‘acb’,‘cac’等字符串。
两个字符串完全匹配才算匹配成功。
时间限制:2000
内存限制:262144
输入
输入为一个数字n表示测试字符串与字符模式对数,换行。(n ≤ 30) 后续2n行为每组匹配的s与p,每行字符串后换行。 s 非空,只包含从 a-z 的小写字母。 p 非空,只包含从 a-z 的小写字母,以及字符 ? 和 *。 字符串s和p的长度均小于50
输出
每一组匹配串匹配成功输出‘yes’,否则输出‘no’。
样例输入
3
abc
abc
abc
a*c
abc
a??c
样例输出
yes
yes
no

下面是使用贪心算法实现的代码示例:

#include 
#include 
#include 

bool isMatch(char *s, char *p) {
    int sLen = strlen(s);
    int pLen = strlen(p);
    int sIndex = 0;  // 字符串s的索引
    int pIndex = 0;  // 字符模式p的索引
    int sStar = -1;  // 字符串s中上一个匹配'*'的位置
    int pStar = -1;  // 字符模式p中上一个匹配'*'的位置

    while (sIndex < sLen) {
        if (pIndex < pLen && (s[sIndex] == p[pIndex] || p[pIndex] == '?')) {
            sIndex++;
            pIndex++;
        } else if (pIndex < pLen && p[pIndex] == '*') {
            sStar = sIndex;
            pStar = pIndex;
            pIndex++;
        } else if (pStar != -1) {
            sStar++;
            sIndex = sStar;
            pIndex = pStar + 1;
        } else {
            return false;
        }
    }

    while (pIndex < pLen && p[pIndex] == '*') {
        pIndex++;
    }

    return pIndex == pLen;
}

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

    while (n--) {
        char s[51];
        char p[51];
        scanf("%s", s);
        scanf("%s", p);

        if (isMatch(s, p)) {
            printf("yes\n");
        } else {
            printf("no\n");
        }
    }

    return 0;
}

这段代码使用贪心算法来进行通配符匹配。在匹配过程中,使用两个索引 sIndexpIndex 分别表示当前匹配到的字符串 s 和字符模式 p 的位置。遍历字符串 s 的每个字符,根据字符模式 p 的当前字符进行匹配。

如果字符模式 p 的当前字符与字符串 s 的当前字符相等,或者字符模式 p 的当前字符为 '?',则匹配成功,将索引 sIndexpIndex 同时向后移动一位。

如果字符模式 p 的当前字符为 '*',则记录下当前位置,并将 pIndex 向后移动一位。

如果匹配失败且上一个匹配到的 p 中存在 '*',则将字符串 s 的索引 sIndex 后移一位,将字符模式 p 的索引 pIndex 重置为上一个 '*' 的下一位,并将上一个匹配到的 s 中的 '*' 的位置 sStar 后移一位。

如果匹配失败且不存在上一个匹配到的 p 中的 '*',则返回 false。

最后,检查字符模式 p 是否已经匹配完毕,如果 pIndex 等于 pLen,则匹配成功;否则,匹配失败。

这段代码实现了贪心算法,并根据题目描述的输入和输出格式进行了相应的处理。你可以将题目给出的示例输入复制到代码中进行测试。

第3题:求逆序对数

对于一个长度为N的整数序列A,满足i < j 且 Ai > Aj.的数对(i,j)称为整数序列A的一个逆序

请求出整数序列A的所有逆序对个数
时间限制:500
内存限制:65536
输入
输入包含多组测试数据,每组测试数据有两行 第一行为整数N(1 <= N <= 20000),当输入0时结束 第二行为N个整数,表示长为N的整数序列
输出
每组数据对应一行,输出逆序对的个数
样例输入
5
1 2 3 4 5
5
5 4 3 2 1
1
1
0
样例输出
0
10
0

下面是使用分治技术实现的代码示例:

#include 
#include 

long long merge(int arr[], int temp[], int left, int mid, int right) {
    int i = left;   // 左子数组索引
    int j = mid + 1;  // 右子数组索引
    int k = left;  // 合并后数组索引
    long long count = 0;  // 逆序对个数

    while (i <= mid && j <= right) {
        if (arr[i] <= arr[j]) {
            temp[k++] = arr[i++];
        } else {
            temp[k++] = arr[j++];
            count += mid - i + 1;  // 统计逆序对个数
        }
    }

    while (i <= mid) {
        temp[k++] = arr[i++];
    }

    while (j <= right) {
        temp[k++] = arr[j++];
    }

    for (int m = left; m <= right; m++) {
        arr[m] = temp[m];
    }

    return count;
}

long long mergeSort(int arr[], int temp[], int left, int right) {
    long long count = 0;

    if (left < right) {
        int mid = (left + right) / 2;
        count += mergeSort(arr, temp, left, mid);
        count += mergeSort(arr, temp, mid + 1, right);
        count += merge(arr, temp, left, mid, right);
    }

    return count;
}

long long countInversions(int arr[], int n) {
    int* temp = (int*)malloc(sizeof(int) * n);
    long long count = mergeSort(arr, temp, 0, n - 1);
    free(temp);
    return count;
}

int main() {
    int n;

    while (scanf("%d", &n) == 1 && n != 0) {
        int* arr = (int*)malloc(sizeof(int) * n);

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

        long long count = countInversions(arr, n);
        printf("%lld\n", count);

        free(arr);
    }

    return 0;
}

这段代码使用分治技术中的归并排序来求逆序对个数。首先实现了一个 merge() 函数,用于合并两个有序子数组,并统计逆序对的个数。然后实现了 mergeSort() 函数,使用递归进行归并排序,并在合并过程中统计逆序对的个数。最后,实现了 countInversions() 函数,调用归并排序函数并返回逆序对的个数。

在主函数中,根据题目要求的输入格式,循环读取多组测试数据。对于每组测试数据,先动态分配一个数组来存储整数序列。然后使用 scanf() 逐个读取整数并存入数组中。调用 countInversions() 函数计算逆序对的个数,并输出结果。

这段代码实现了分治技术,并根据题目描述的输入和输出格式进行了相应的处理。你可以将题目给出的示例输入复制到代码中进行测试。

第4题:分成互质组

给定n个正整数,将它们分组,使得每组中任意两个数互质。至少要分成多少个组?
时间限制:1000
内存限制:65536
输入
第一行是一个正整数n。1 <= n <= 10。 第二行是n个不大于10000的正整数。
输出
一个正整数,即最少需要的组数。
样例输入
6
14 20 33 117 143 175
样例输出
3

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

#include 
#include 

bool isCoprime(int a, int b) {
    // 辗转相除法判断两个数是否互质
    while (b != 0) {
        int temp = b;
        b = a % b;
        a = temp;
    }

    return a == 1;
}

void dfs(int nums[], int n, int group[], int index, int count, int *minCount) {
    if (index == n) {
        if (count < *minCount) {
            *minCount = count;
        }
        return;
    }

    // 尝试将当前数放入已有的组中
    for (int i = 0; i < count; i++) {
        if (isCoprime(nums[index], nums[group[i]])) {
            group[i] = index;
            dfs(nums, n, group, index + 1, count, minCount);
            group[i] = -1;  // 回溯
        }
    }

    // 尝试新建一组
    group[count] = index;
    dfs(nums, n, group, index + 1, count + 1, minCount);
    group[count] = -1;  // 回溯
}

int minGroups(int nums[], int n) {
    int group[10];  // 存储分组信息
    for (int i = 0; i < 10; i++) {
        group[i] = -1;  // 初始化为-1
    }

    int minCount = n;  // 最少组数初始化为n
    dfs(nums, n, group, 0, 0, &minCount);
    return minCount;
}

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

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

    int minCount = minGroups(nums, n);
    printf("%d\n", minCount);

    return 0;
}

这段代码使用搜索剪枝技术来求解将给定的正整数分成互质组的最少组数。首先实现了 isCoprime() 函数,用于判断两个数是否互质。然后实现了 dfs() 函数,使用深度优先搜索来尝试将每个数放入已有的组中或新建一组,并更新最少组数。最后,实现了 minGroups() 函数,调用深度优先搜索函数并返回最少组数。

在主函数中,根据题目要求的输入格式,先读取正整数的个数 n。然后使用 scanf() 逐个读取正整数并存入数组中。调用 minGroups() 函数计算最少组数,并输出结果。

这段代码实现了搜索剪枝技术,并根据题目描述的输入和输出格式进行了相应的处理。你可以将题目给出的示例输入复制到代码中进行测试。

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