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

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

第1题:最佳路径

如下所示的由正整数数字构成的三角形:
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
从三角形的顶部到底部有很多条不同的路径。对于每条路径,把路径上面的数加起来可以得到一个和,和最大的路径称为最佳路径。你的任务就是求出最佳路径上的数字之和。
注意:路径上的每一步只能从一个数走到下一层上和它最近的下边(正下方)的数或者右边(右下方)的数。
时间限制:1000
内存限制:65536
输入
第一行为三角形高度100>=h>=1,同时也是最底层边的数字的数目。 从第二行开始,每行为三角形相应行的数字,中间用空格分隔。
输出
最佳路径的长度数值。
样例输入
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
样例输出
30
提示
如何采用动态规划的思想,对问题进行分解。

这个问题可以使用动态规划来解决。我们可以定义一个二维数组dp,其中dp[i][j]表示从三角形顶部到第i行第j列的最佳路径的数字之和。

根据题目的要求,对于每个位置(i, j),可以从上方的位置(i-1, j-1)或者(i-1, j)到达。因此,状态转移方程可以表示为:

dp[i][j] = max(dp[i-1][j-1], dp[i-1][j]) + triangle[i][j]

其中,triangle[i][j]表示三角形第i行第j列的数字。

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

#include 
#include 

#define MAX_HEIGHT 100

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

int calculateMaxPath(int height, int triangle[][MAX_HEIGHT]) {
    int dp[MAX_HEIGHT][MAX_HEIGHT];

    // 初始化dp数组
    dp[0][0] = triangle[0][0];
    for (int i = 1; i < height; i++) {
        dp[i][0] = dp[i - 1][0] + triangle[i][0];
        dp[i][i] = dp[i - 1][i - 1] + triangle[i][i];
    }

    // 计算最佳路径的数字之和
    for (int i = 2; i < height; i++) {
        for (int j = 1; j < i; j++) {
            dp[i][j] = max(dp[i - 1][j - 1], dp[i - 1][j]) + triangle[i][j];
        }
    }

    // 找出最大路径的数字之和
    int maxSum = 0;
    for (int j = 0; j < height; j++) {
        maxSum = max(maxSum, dp[height - 1][j]);
    }

    return maxSum;
}

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

    int triangle[MAX_HEIGHT][MAX_HEIGHT];
    for (int i = 0; i < height; i++) {
        for (int j = 0; j <= i; j++) {
            scanf("%d", &triangle[i][j]);
        }
    }

    int result = calculateMaxPath(height, triangle);
    printf("%d\n", result);

    return 0;
}

在主函数中,我们首先读取输入的三角形的高度height,然后读取三角形的数字并存储在二维数组triangle中。接下来,我们调用calculateMaxPath函数计算最佳路径的数字之和,并将结果输出。

第2题:邮票收集

小A是个邮票收集爱好家,他有n种面值的邮票,每种邮票都有无数张。一天小B想要寄信,需要一共面值和为k的邮票组合。小A想要知道拼出面值为k的邮票最少需要多少张。
时间限制:1000
内存限制:131072
输入
输入是多组数据。(不超过10组) 每组数据的第一行正整数n,k,表示邮票的种类数目和目标要拼出的钱。(0 < n ≤ 100, 0 < k ≤ 1000 ) 接下来的一行有n个正整数ai(0 < ai ≤ 1000)。 若n=k=0表示输入结束。
输出
每组数据输出一行一个数,分别表示拼出k需要的最少的邮票数量。 如果不存在能够拼出k的方案,输出-1。
样例输入
4 10
1 2 3 4
5 16
1 2 3 4 5
2 7
4 5
0 0
样例输出
3
4
-1
提示
第一组数据: 10 = 4+4+2 第二组数据:16 = 5+5+5+1 第三组数据: 不存在。

这个问题可以使用动态规划来解决。我们可以定义一个一维数组dp,其中dp[i]表示拼出面值为i的邮票所需的最少数量。

根据题目的要求,对于每个金额i,我们可以考虑选择每种面值的邮票,然后更新最少数量。状态转移方程可以表示为:

dp[i] = min(dp[i], dp[i - a[j]] + 1), 其中 0 <= j < n

其中,a[j]表示第j种面值的邮票。

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

#include 
#include 
#include 

#define MAX_N 100
#define MAX_K 1000

int min(int a, int b) {
    return (a < b) ? a : b;
}

int calculateMinStamps(int n, int k, int stamps[]) {
    int dp[MAX_K + 1];

    // 初始化dp数组
    dp[0] = 0;
    for (int i = 1; i <= k; i++) {
        dp[i] = INT_MAX;
    }

    // 计算最少邮票数量
    for (int i = 1; i <= k; i++) {
        for (int j = 0; j < n; j++) {
            if (i >= stamps[j] && dp[i - stamps[j]] != INT_MAX) {
                dp[i] = min(dp[i], dp[i - stamps[j]] + 1);
            }
        }
    }

    return (dp[k] == INT_MAX) ? -1 : dp[k];
}

int main() {
    int n, k;

    while (scanf("%d %d", &n, &k) == 2) {
        if (n == 0 && k == 0) {
            break;  // 输入结束
        }

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

        int result = calculateMinStamps(n, k, stamps);
        printf("%d\n", result);
    }

    return 0;
}

在主函数中,我们使用一个循环来读取多组数据。对于每组数据,我们首先读取邮票的种类数目n和目标金额k,然后读取每种面值的邮票,并将它们存储在数组stamps中。接下来,我们调用calculateMinStamps函数计算拼出目标金额所需的最少邮票数量,并将结果输出。

第3题:切割回文

阿福最近对回文串产生了非常浓厚的兴趣。
如果一个字符串从左往右看和从右往左看完全相同的话,那么就认为这个串是一个回文串。例如,“abcaacba”是一个回文串,“abcaaba”则不是一个回文串。
阿福现在强迫症发作,看到什么字符串都想要把它变成回文的。阿福可以通过切割字符串,使得切割完之后得到的子串都是回文的。
现在阿福想知道他最少切割多少次就可以达到目的。例如,对于字符串“abaacca”,最少切割一次,就可以得到“aba”和“acca”这两个回文子串。
时间限制:1000
内存限制:65536
输入
输入的第一行是一个整数 T (T <= 20) ,表示一共有 T 组数据。 接下来的 T 行,每一行都包含了一个长度不超过的 1000 的字符串,且字符串只包含了小写字母。
输出
对于每组数据,输出一行。该行包含一个整数,表示阿福最少切割的次数,使得切割完得到的子串都是回文的。
样例输入
3
abaacca
abcd
abcba
样例输出
1
3
0
提示
对于第一组样例,阿福最少切割 1 次,将原串切割为“aba”和“acca”两个回文子串。 对于第二组样例,阿福最少切割 3 次,将原串切割为“a”、“b”、“c”、“d”这四个回文子串。 对于第三组样例,阿福不需要切割,原串本身就是一个回文串。

这个问题可以使用动态规划来解决。我们可以定义一个一维数组dp,其中dp[i]表示前i个字符组成的子串最少需要切割几次才能使得子串都是回文的。

根据题目的要求,对于每个位置i,我们可以考虑将子串切割为两部分,前半部分为回文子串,后半部分为回文子串。如果前半部分是回文子串,那么我们只需要判断后半部分是否是回文子串,即判断dp[j] + 1是否更小。状态转移方程可以表示为:

dp[i] = min(dp[i], dp[j] + 1), 其中 j < i 且 s[j+1...i]是回文子串

其中,s[j+1...i]表示字符串中从位置j+1到位置i的子串。

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

#include 
#include 
#include 

#define MAX_LEN 1000

int min(int a, int b) {
    return (a < b) ? a : b;
}

int isPalindrome(char str[], int start, int end) {
    while (start < end) {
        if (str[start] != str[end]) {
            return 0;  // 不是回文串
        }
        start++;
        end--;
    }
    return 1;  // 是回文串
}

int calculateMinCut(char str[]) {
    int len = strlen(str);
    int dp[MAX_LEN];
    int isPal[MAX_LEN][MAX_LEN];

    // 初始化dp数组和isPal数组
    for (int i = 0; i < len; i++) {
        dp[i] = i;  // 最多切割i次
        for (int j = 0; j < len; j++) {
            isPal[i][j] = 0;
        }
    }

    // 计算最少切割次数
    for (int i = 0; i < len; i++) {
        for (int j = 0; j <= i; j++) {
            if (str[i] == str[j] && (i - j <= 2 || isPal[j + 1][i - 1])) {
                isPal[j][i] = 1;
                if (j == 0) {
                    dp[i] = 0;  // 整个子串是回文串,不需要切割
                } else {
                    dp[i] = min(dp[i], dp[j - 1] + 1);
                }
            }
        }
    }

    return dp[len - 1];
}

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

    while (T--) {
        char str[MAX_LEN];
        scanf("%s", str);

        int result = calculateMinCut(str);
        printf("%d\n", result);
    }

    return 0;
}

在主函数中,我们首先读取整数T,表示有T组数据。然后,使用一个循环读取每组数据的字符串,并调用calculateMinCut函数计算最少切割次数,并将结果输出。

第4题:小球放盒子

有N个相同的球,M个不同的盒子,每个盒子最多放K个球
请计算将这N个球全部放入盒子中的方案数模1000007后的结果
时间限制:10000
内存限制:131072
输入
三个正整数,依次为N,M,K
输出
输出方案数模1000007后的结果
样例输入
4 2 3
样例输出
3
提示
总共有3种方案,依次为 { 3 , 1 },{ 2 , 2 },{ 1 , 3 }。 对于100%的数据, N,M ≤ 5000

这个问题可以使用组合数学的知识来解决。我们需要计算将N个球放入M个盒子中的方案数。

首先,考虑将N个球放入一个盒子中的方案数。由于每个盒子最多放K个球,我们可以使用0个、1个、2个…K个球来填充这个盒子。因此,对于一个盒子,可以有K+1种放球的方案。

接下来,考虑将N个球放入M个盒子中的方案数。我们可以将问题转化为将N个球放入M个盒子中,每个盒子至少放1个球的方案数。假设我们已经将每个盒子放入了一个球,那么剩余的N-M个球可以按照第一个考虑的情况放入这M个盒子中。根据乘法原理,将N-M个球放入M个盒子中的方案数为(M+1)^(N-M)。

因此,将N个球放入M个盒子中的方案数为(M+1)^(N-M)。最终的结果需要对1000007取模。

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

#include 

int powerMod(int base, int exponent, int mod) {
    int result = 1;
    while (exponent > 0) {
        if (exponent % 2 == 1) {
            result = (result * base) % mod;
        }
        base = (base * base) % mod;
        exponent /= 2;
    }
    return result;
}

int calculateBallInBox(int N, int M, int K) {
    int result = powerMod(M + 1, N - M, 1000007);
    return result;
}

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

    int result = calculateBallInBox(N, M, K);
    printf("%d\n", result);

    return 0;
}

在主函数中,我们首先读取整数N、M、K,分别表示球的数量、盒子的数量和每个盒子最多放置的球的数量。然后,调用calculateBallInBox函数计算将N个球放入M个盒子中的方案数,并将结果输出。

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