中国电子学会(CEIT)考评中心历届真题(含详细解析答案)
C语言软件编程等级考试四级 2020年06月
编程题四道 总分:100分
一、最长上升子序列(25分)
一个数的序列bi,当b1 < b2< … <bs的时候,我们称这个序列是上升的。对于给定的一个序列(a1, a2… aN),我们可以得到一些上升的子序列(ai1, ai2,…, aik),这里1<=i1<i2<… <ik <=N。
比如,对于序列(1,7,3,5,9,4,8),有它的一些上升子序列,如(1,7),(3,4,8)等等。这些子序列中最长的长度是4,比如子序列(1,3,5,8)。
你的任务,就是对于给定的序列,求出最长上升子序列的长度。
时间限制: 2000ms
内存限制: 65536kb
输入
输入的第一行是序列的长度N (1 <=N<= 1000)。
第二行给出序列中的N个整数,这些整数的取值范围都在0到10000。输出
最长上升子序列的长度。
样例输入
7
1 7 3 5 9 4 8
样例输出
4
#include <iostream> // 引入输入输出流库
using namespace std; // 使用标准命名空间int main() { // 程序主入口int N; // 定义一个整型变量N,用于存储用户输入的序列长度scanf("%d",&N); // 从标准输入读取一个整数到变量Nint a[1010]; // 定义一个整型数组a,大小为1010,用于存储用户输入的序列for(int i = 0; i < N; i++){ // 循环N次,读取序列中的每个元素scanf("%d", &a[i]); // 从标准输入读取一个整数到数组a的第i个位置}// 定义dp数组,dp[i]表示以a[i]结尾的最长上升子序列的长度int dp[1010]; dp[0] = 1; // 初始化dp数组的第一个元素为1,因为任何单个元素都可以视为一个长度为1的上升子序列int max_len = 0; // 定义一个变量max_len,用于存储最长上升子序列的长度for(int i = 1; i < N; i++){ // 从序列的第二个元素开始遍历for(int j = i - 1; j >= 0; j--){ // 对每个元素,向前遍历它之前的所有元素if(a[i] > a[j]){ // 如果当前元素大于之前的某个元素dp[i] = dp[j] + 1; // 更新dp[i]为dp[j]+1,表示以a[i]结尾的最长上升子序列长度增加了1break; // 找到后,跳出内层循环}}max_len = max(max_len, dp[i]); // 更新最长上升子序列的长度}printf("%d", max_len); // 输出最长上升子序列的长度return 0; // 程序结束,返回0
}/*代码使用动态规划的思想解决了LIS问题。对于每个位置i,它都尝试找到在它之前的位置j,使得a[i] > a[j],并更新dp[i]为dp[j] + 1。这样,dp[i]就存储了以a[i]结尾的最长上升子序列的长度。然后,通过遍历所有的dp[i],可以找到整个序列的最长上升子序列的长度。
*/
二、核电站(25分)
一个核电站有N个放核物质的坑,坑排列在一条直线上。如果连续3个坑中放入核物质,则会发生爆炸,于是,在某些坑中可能不放核物质。
现在,请你计算:对于给定的N,求不发生爆炸的放置核物质的方案总数。
时间限制: 1000ms
内存限制: 131072kb
输入
输入文件只有多行,每行对应一个正整数N<= 40;输出
输出文件有多行,每行只有一个正整数,表示方案总数。
样例输入
1
2
3
4
10
样例输出
2
4
7
13
504
#include <iostream> // 包含C++标准输入输出流库
#include <cstring> // 包含C++字符串操作库,用于memset函数
using namespace std; // 使用标准命名空间long long dp[55][7]; // 定义一个二维数组dp,用于存储动态规划的状态。dp[i][j]表示前i个坑中连续埋了j个雷的方案数。int main() {int n; // 定义一个整数n,表示坑的数量for(int c = 0; c<40; c++){ // 循环40次,处理多组数据cin >> n; // 从标准输入读取一个整数nif(n <= 0) // 如果n小于等于0,则结束当前循环break;memset(dp,0, sizeof(dp)); // 将dp数组中的所有元素初始化为0dp[1][1] = 1; // 第一个坑放一个核物质的方案数为1dp[1][0] = 1; // 第一个坑不放核物质的方案数为1for(int i = 2; i <= n; i++){ // 从第二个坑开始遍历到第n个坑for(int j = 0; j < 3; j++){ // 遍历连续放核物质的数量,从0到2if(j == 0){ // 如果当前连续放核物质的数量为0for(int k = 0; k < 3;k++) // 遍历前一个坑连续放核物质的数量,从0到2dp[i][j]+= dp[i - 1][k]; // 更新dp[i][j],累加前一个坑所有可能的方案数}else // 如果当前连续放核物质的数量不为0dp[i][j] += dp[i - 1][j - 1]; // 更新dp[i][j],累加前一个坑连续埋了j-1个核物质的方案数}}long long ans = 0; // 定义一个变量ans,用于存储所有可能的方案数for(int i = 0; i < 3; i++) // 遍历最后一个坑连续放核物质的数量,从0到2ans += dp[n][i]; // 累加最后一个坑所有可能的方案数到anscout <<ans; // 输出所有可能的方案数到标准输出}return 0; // 程序结束,返回0
}/*代码使用动态规划的方法解决了放置物体的问题。首先,它初始化了一个二维数组dp来存储中间结果。然后,它读取一系列的输入值n,对于每个n,它都使用动态规划来计算在n个位置中放置物体的方案数,并将结果输出到标准输出。
*/
三、山区建小学(25分)
政府在某山区修建了一条道路,恰好穿越总共m个村庄的每个村庄一次,没有回路或交叉,任意两个村庄只能通过这条路来往。
已知任意两个相邻的村庄之间的距离为di(为正整数),其中,0 < i < m。为了提高山区的文化素质,政府又决定从m个村中选择n个村建小学(设0 < n <= m < 500 )。
请根据给定的m、n以及所有相邻村庄的距离,选择在哪些村庄建小学,才使得所有村到最近小学的距离总和最小,计算最小值。
时间限制: 24000ms
内存限制: 65536kb
输入
第1行为m和n,其间用空格间隔;
第2行为(m-1)个整数,依次表示从一端到另一端的相邻村庄的距离,整数之间以空格间隔。
例如: 10 3 2 4 6 5 2 4 3 1 3,表示在10个村庄建3所学校。第1个村庄与第2个村庄距离为2,第2个村庄与第3个村庄距离为4,第3个村庄与第4个村庄距离为6,…,第9个村庄到第10个村庄的距离为3。
输出
各村庄到最近学校的距离之和的最小值。
样例输入
10 2
3 1 3 1 1 1 1 1 3
样例输出
18
#include <algorithm> // 引入算法库,尽管在这段代码中并没有使用到该库中的功能
#include <cstring> // 引入字符串操作库,用于memset函数
#include <iostream> // 引入输入输出流库
using namespace std; // 使用标准命名空间int dis[510], a[510][510], dp[510][510]; // 定义全局变量:dis用于存储距离,a用于存储某个子问题的解,dp用于存储最终的最优解int main() { // 主函数入口int m, n; // 定义两个整数变量m和n,分别表示问题的两个维度memset(a,0,sizeof(a)); // 使用memset函数将二维数组a初始化为0cin >> m >> n; // 从标准输入读取m和n的值dis[1] = 0; // 初始化dis数组的第一个元素为0for (int i = 2; i<= m; i++){ // 从2开始遍历到mcin >> dis[i]; // 读取dis数组的每个元素dis[i]+= dis[i - 1]; // 更新dis数组,使其存储的是从第一个元素到当前元素的累积和}for (int i = 1; i <= m; i++) // 遍历所有可能的子问题的起始点for (int j = i + 1; j <= m; j++) // 遍历所有可能的子问题的结束点a[i][j] = a[i][j - 1] + dis[j] - dis[(i + j)/ 2]; // 计算子问题的解,这里使用了一个基于累积和的公式for (int i = 1; i <= m; i++) // 初始化dp数组的第一列for(int j = 1; j <= i && j <= n; j++)dp[i][j] = 999999; // 将dp数组初始化为一个很大的数,表示初始时还没有找到解for (int i =1; i<= m; i++) // 初始化dp数组的第一行dp[i][1] = a[1][i]; // 将dp数组的第一行设置为a数组的第一行的值for (int i = 2; i <= m; i++) // 从第二行开始遍历dp数组for(int j = 2; j <= i && j <= n; j++) // 遍历dp数组的每一列for(int k = j - 1; k < i; k++) // 遍历所有可能的分割点kdp[i][j] = min(dp[i][j], dp[k][j - 1] + a[k + 1][i]); // 更新dp数组的值,寻找最优解cout << dp[m][n] << endl; // 输出最终的最优解return 0; // 程序结束
}/*这个程序的整体逻辑是动态规划。首先,它计算了一个累积和数组dis,然后基于这个数组计算了子问题的解a。接着,它使用动态规划的思想,通过填充dp数组来找到最终的最优解。最后,程序输出了这个最优解。
*/
四、公共子序列(25分)
我们称序列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的子序列。
时间限制: 3000ms
内存限制: 65536kb
输入
输入包括多组测试数据。每组数据包括一行,给出两个长度不超过200的字符串,表示两个序列。两个字符串之间由若干个空格隔开。
输出
对每组输入数据,输出一行,给出两个序列的最大公共子序列的长度。
样例输入
abcfbc abfcab
programming contest
abcd mnp
样例输出
4
2
0
#include<iostream> // 引入输入输出流库
#include<cstring> // 引入字符串操作库(这里主要是使用memset函数)
#include<cstdio> // 引入C标准输入输出库(虽然在这段代码中未直接使用)
using namespace std; // 使用标准命名空间string a,b; // 定义两个字符串变量a和b
int dp[220][220]; // 定义一个二维数组dp,用于存储动态规划过程中的子问题的解int lcs(){ // 定义一个函数lcs,用于计算最长公共子序列的长度memset(dp,0,sizeof(dp)); // 使用memset函数初始化dp数组,所有元素设为0int lena=a.size(),lenb=b.size(); // 获取字符串a和b的长度for(int i=1;i<=lena;i++) // 外层循环,遍历字符串a的所有字符for(int j=1;j<=lenb;j++) // 内层循环,遍历字符串b的所有字符if(a[i-1]==b[j-1]) // 如果当前字符相等dp[i][j]=dp[i-1][j-1]+1; // 更新dp[i][j]为左上角元素加1else // 如果当前字符不相等dp[i][j]=max(dp[i-1][j],dp[i][j-1]); // 更新dp[i][j]为上方和左方元素中的较大值return dp[lena][lenb]; // 返回dp数组的最后一个元素,即最长公共子序列的长度
}int main() { // 主函数while(cin>>a>>b) // 循环读取字符串a和bcout<<lcs()<<"\n"; // 调用lcs函数并输出最长公共子序列的长度,然后换行return 0; // 程序结束
}
/*这道题使用了动态规划的方法来求解最长公共子序列问题。通过构建一个二维数组dp来存储子问题的解,并利用递推关系逐步填充这个数组,最终得到最长公共子序列的长度。
*/