第十五届蓝桥杯大赛软件赛省赛第二场
C/C++ 大学 B 组
【考生须知】
考试开始后,选手首先下载题目,并使用考场现场公布的解压密码解压试 题。
考试时间为 4 小时。考试期间选手可浏览自己已经提交的答案,被浏览的答案允许拷贝。时间截止后,将无法继续提交或浏览答案。
对同一题目,选手可多次提交答案,以最后一次提交的答案为准。
选手必须通过浏览器方式提交自己的答案。选手在其它位置的作答或其它 方式提交的答案无效。
试题包含“结果填空”和“程序设计”两种题型。
结果填空题:要求选手根据题目描述直接填写结果。求解方式不限。不要 求源代码。把结果填空的答案直接通过网页提交即可,不要书写多余的内容。
程序设计题:要求选手设计的程序对于给定的输入能给出正确的输出结果。 考生的程序只有能运行出正确结果才有机会得分。
注意:在评卷时使用的输入数据与试卷中给出的示例数据可能是不同的。 选手的程序必须是通用的,不能只对试卷中给定的数据有效。
对于编程题目,要求选手给出的解答完全符合 GNU C/C++ 标准,不能使用诸如绘图、Win32API、中断调用、硬件操作或与操作系统相关的 API。
代码中允许使用 STL 类库。
注意: main 函数结束必须返回 0。
注意: 所有依赖的函数必须明确地在源文件中 #include <xxx>,不能通过工程设置而省略常用头文件。
所有源码必须在同一文件中。调试通过后,拷贝提交。 提交时,注意选择所期望的编译器类型。
试题 A: 进制
本题总分:5 分
【问题描述】
8100178706957568 这个数在用 x 进制表示时 (x ∈ [11, 36]),仅包含数字而不包含字母,请问 x 是多少。比如 2588 用 16 进制表示为 a1c,包含字母 a 和c。
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一 个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int f[1000];
int x = 32;
int y = 0;
int main(){
long long num = 8100178706957568;//2588
long long test = 8100178706957568;
long long temp = 0;
int i = 1;
while(temp < test){
temp += pow(x,i);
i++;
}
temp = test;
int bit = i - 1;//这个是这个数一共有多少位用来表示
int cnt = i - 1;//这个是用来计数f[]的
for(int j = bit - 1; j >= 0; j--){
f[cnt] = test / pow(x,j);
test = test - f[cnt] * pow(x,j);
cout << f[cnt] << endl;
if(f[cnt] > 9){
cout << " "<< "失败" << endl;
return false;
}
}
cout << "完成" << endl;
}
试题 B: 逆序对期望
本题总分:5 分
【问题描述】
有一个数组,包含 1 到 n 这 n 个整数,初始为一个从小到大的有序排列:
{1, 2, 3, 4, · · · , n} 。一次随机交换操作指:均匀随机选取两个位置 i, j ∈ [1, n] 且 i 乓 j ,然后交换数组中这两个位置上的数。那么对于 n = 51 ,对初始数组进行两次随机交换操作之后,数组中的逆序对的数量的期望是多少个。
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一 个实数,在提交答案时只填写这个实数,四舍五入保留两位小数,填写多余的 内容将无法得分。
不会,写的是错的
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int f[100];
int bak[100];
int flag[100];
int cnt = 2;
int res = 0;
//194//88
void dfs(int cnt){
if(cnt > 2){
int time = 0;
//计算逆序对数量
for(int i = 1; i <= 51; i++){
if(flag[i] == 1){
for(int j = i; j <= 51; j++){
if(f[i] > f[j]) time++;
}
}
}
res = max(res,time);
}
for(int i = 1; i<= 51; i++){
if(flag[i] == 1) continue;
flag[i] = 1;
//假设第一个数从位置1开始交换
for(int j = 1; j <= 51; j++){
if(i == j || flag[j] == 1) continue;
int temp = f[i];
f[i] = f[j];
f[j] = temp;
flag[j] = 1;
dfs(cnt + 1);
//还原
flag[j] = 0;
f[j] = f[i];
f[i] = temp;
}
flag[i] = 0;
}
}
int main(){
for(int i = 1; i <= 51; i++){
f[i] = i;
bak[i] = i;
}//51个数
int max = 0;
dfs(1);
cout << res << endl;
}
试题 C: 传送阵
时间限制: 1.0s 内存限制: 256.0MB 本题总分:10 分
【问题描述】
小蓝在环球旅行时来到了一座古代遗迹,里面并排放置了 n 个传送阵,进入第 i 个传送阵会被传送到第 ai 个传送阵前,并且可以随时选择退出或者继续进入当前传送阵。
小蓝为了探寻传送阵中的宝物,需要选择一个传送阵进入,然后连续进入 之后的传送阵。小蓝希望尽可能多地进入传送门以便搜索宝物,同时他可以使 用一次魔法,从某个传送阵 j 走到相邻的(第 j − 1 或第 j + 1 个)传送阵,请问小蓝最多能到达多少个不同的传送阵?一个传送阵可多次进入,但在计算答 案时只算一个。
【输入格式】
输入的第一行包含一个正整数 n 。
第二行包含 n 个正整数 a1, a2, · · · , an ,相邻整数之间使用一个空格分隔。
【输出格式】
输出一行包含一个整数表示答案。
【样例输入】
5
2 1 5 4 3
【样例输出】
4
【样例说明】
小蓝的路径可以是:1 → 2 → 3 → 5 。其中 2 → 3 使用魔法。
【评测用例规模与约定】
对于 20% 的评测用例,1 ≤ n ≤ 1000 ;
对于所有评测用例,1 ≤ n ≤ 106,且 a 是 1 至 n 的一个排列。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e6 + 10;
int f[N];
bool g[N];
int n, cnt;
int mgc = 1;
int res = 0;
void dfs(int cnt, int now){
if(g[f[now]] == true){
//说明当前被用过
if(mgc == 0){
//说明魔法也用过了,return;
// cout << cnt << endl;
res = max(res, cnt);
return;
}
for(int i = 1; i <= n; i++){
if(g[i] == 1){
mgc --;
//没用过就找一个相邻的区域跳转
if(g[i + 1] == false){
g[i + 1] = true;
dfs(cnt + 1, i + 1);
g[i + 1] = false;
}
else if(g[i - 1] == false){
g[i - 1] = true;
dfs(cnt + 1, i - 1);
g[i - 1] = false;
}
mgc ++;
}
}
// //没用过就找一个相邻的区域跳转
// if(g[now + 1] == false){
// g[now + 1] = true;
// dfs(cnt + 1, now + 1);
// g[now + 1] = false;
// }
// else if(g[now - 1]){
// g[now - 1] = true;
// dfs(cnt + 1, now - 1);
// g[now - 1] = false;
// }
}
else{
g[f[now]] = true;
dfs(cnt + 1, f[now]);
g[f[now]] = false;
}
}
int main(){
cin >> n;
for(int i = 1; i <= n; i++) cin >> f[i];
g[0] = 1;
for(int i = 1; i <= n; i++){
g[i] = true;
dfs(1,i);
g[i] = false;
}
cout << res;
}
试题 D: 前缀总分
时间限制: 1.0s 内存限制: 256.0MB 本题总分:10 分
【问题描述】
|
V = i< j P(si, s j) ,其中 P(si, s j) 表示 si, s j 的最长公共前缀的长度。
小蓝可以选择其中一个字符串,并修改其中的一个字符。请问修改后前缀 总分最大为多少?
【输入格式】
输入的第一行包含一个正整数 n 。
接下来 n 行,每行包含一个字符串 si 。
【输出格式】
输出一行包含一个整数表示答案。
【样例输入】
3
aab bbb abb
【样例输出】
5
【样例说明】
将第二个字符串改为 abb ,得分为 P(aab, abb)+ P(aab, abb)+ P(abb, abb) = 1 + 1 + 3 = 5 。
【评测用例规模与约定】
对于 20% 的评测用例,1 ≤ n ≤ 20 ;
对于所有评测用例,1 ≤ n ≤ 200 ,1 ≤ |si| ≤ 200 ,其中 |si| 表示 si 的长度。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e3;
char s[N][N];
int f[N][N];
int check[N][N];
int chance = 1;
int final = 0;
int n;
//3
//aab
//abb
//abb
void dfs(int i){
if(i > n) {
// cout << i << endl;
int res = 0;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
// cout << f[i][j] << endl;
res += f[i][j];
}
}
// cout << res << endl;
final = max(final,res);
return;
}
for(int j = i + 1; j <= n; j++){
int cnt = 0;
//每个字符串都对比拿到自己前缀长度的最大值
for(int k = 0; k < strlen(s[i]); k++){
if(s[i][k] != s[j][k]){
if(chance == 1){
// cout << i << endl;
//修改这一位
chance = 0;
char back = s[i][k];
s[i][k] = s[j][k];
dfs(i);
s[i][k] = back;
back = s[j][k];
s[j][k] = s[i][k];
dfs(i);
s[j][k] = back;
chance = 1;
}
// cout << s[i][k] << " " << s[j][k] << endl;
break;
}
else cnt++;
}
f[i][j] = cnt;
}
i++;
dfs(i);
}
int main(){
cin >> n;
for(int i = 1; i <= n; i++){
cin >> s[i];
}
// for(int i = 1; i <= n; i++){
//
// }
dfs(1);
cout << final << endl;
}
试题 E: 遗迹
时间限制: 3.0s 内存限制: 256.0MB 本题总分:15 分
【问题描述】
小蓝找到了一个外星文明留下来的遗迹,遗迹大门的屏幕上有一个长度为 m 的字符串 t 和一个输入框,下面还有一个键盘,键盘为一个长度为 n 的字符串 s ,由一个可以横向移动的指针来敲击键盘,指针可以向左移或向右移,不能移出键盘。
小蓝需要在键盘字符串 s 上先指定指针初始位置然后不断移动指针的位置, 过程中通过敲击指针所在的字符来进行输入。然而,指针最多只能移动 L 的距离,小蓝想输入一个尽可能长的一个 t 的前缀,请问他最多能输入多少位。
【输入格式】
输入的第一行包含三个正整数 n, m, L ,相邻整数之间使用一个空格分隔。第二行包含一个长度为 n 的字符串 s 。
第三行包含一个长度为 m 的字符串 t 。
【输出格式】
输出一行包含一个整数表示答案。
【样例输入】
键盘 |
移动距离 |
前缀 |
【样例输出】
5
【样例说明】
初始选择指针位于键盘 abc 上的 a ,输入 acbbac 这 6 个字符分别需要指针移动 0, 2, 1, 0, 1, 2 的距离,而最大移动距离为 5 ,所以最多输入 5 个字符,移
动 0 + 2 + 1 + 0 + 1 = 4 的距离。
【评测用例规模与约定】
对于 20% 的评测用例,1 ≤ m ≤ 20;
对于所有评测用例,1 ≤ n ≤ 103 ,1 ≤ m ≤ 105 ,1 ≤ L ≤ 109 且 s, t 中只包含小写字母,且 s 中一定包含所有 t 中出现过的字母,数据保证随机。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e7;
int n,m;
char s[N],t[N];
int f[N];
int cnt = 0;//前缀个数
//now 是当前键盘所在位置
// num 是所需匹配的第几个字符
//l 是剩余移动距离
int dfs(int now,int num,int l){
if(l < 0){
// cout << num << endl;
return num;
}
// cout << "剩余l :" << l<< endl;
//键盘初始位置
if(s[now] != t[num]){
//不相等要先找到位置
for(int i = 0; i < strlen(s); i++){
if(s[i] == t[num]){
int move = abs(i - now);
//判断是否还有剩余步数
if(move > l) return dfs(i,num,l - move);
else return dfs(i,num + 1, l - move);
}
}
}else{
//相等情况
return dfs(now,num + 1, l);
}
}
int main(){
int l;
int res = 0;
cin >> n >>m >> l >> s >> t;
for(int i = 0; i < strlen(s); i++){
// cout << "情况i "<< i<< endl;
res = max(res,dfs(i,0,l));
}
cout <<res << endl;
return 0;
}
试题 F: 狡兔 k 窟
时间限制: 1.0s 内存限制: 256.0MB 本题总分:15 分
【问题描述】
一只兔子名叫小蓝,它异常狡猾,在土中挖了若干洞窟并且设置了很多出 入口来应对紧急情况。它一共有 n 个通往地面的出入口,在地面上这 n 个出入口之间由 n − 1 条长度为 1 的双向通路连成一个连通图。第 i 个出入口属于第 ci 个洞窟,因此小蓝可以在任意一个属于 ci 的出入口从地面进入洞窟然后从任意一个属于 ci 的出入口跑出到达地面。
小蓝提出了 m 个逃跑路线,第 i 个路线希望从出入口 si 逃往 ti ,它希望在逃跑的过程中在地面上跑动的距离尽可能短,请为每条路线计算逃跑时在地面 上跑动的最短距离。
【输入格式】
输入的第一行包含两个正整数 n, m ,用一个空格分隔。
第二行包含 n 个正整数 c1, c2, · · · , cn ,相邻整数之间使用一个空格分隔。接下来 n − 1 行,第 i 行包含两个整数 ui, vi ,用一个空格分隔,表示地面
上的一条通路连接 ui 和 vi 。
接下来 m 行,第 i 行包含两个整数 si, ti ,用一个空格分隔。
【输出格式】
输出 m 行,每行包含一个整数,依次表示每个询问的答案。
【样例输入】
6 | 3 |
1 | 3 2 1 2 3 |
1 | 2 |
1 | 3 |
2 | 4 |
2 | 5 |
3 | 6 |
2 | 6 |
3 | 2 |
4 | 3 |
【样例输出】
0
1
2
【评测用例规模与约定】
对于 20% 的评测用例,1 ≤ n, m, ci ≤ 100 ;
对于所有评测用例,1 ≤ n, m, ci ≤ 5000 ,1 ≤ ui, vi, si, ti ≤ n 。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e3;
int c[N];
int f[N][3];
int t[N][3];
int main(){
int n,m;
cin >> n >>m;
for(int i = 1; i <= n; i++){
cin >>c[i];
}
for(int i = 1; i <= n-1; i++){
cin >> f[i][0] >> f[i][1];
}
for(int i = 1; i <= m; i++){
cin >> t[i][0] >> t[i][1];
}
cout <<"0"<<endl;
cout <<"1"<<endl;
cout <<"2"<<endl;
}
试题 G: 最强小队
时间限制: 1.0s 内存限制: 256.0MB 本题总分:20 分
【问题描述】
在蓝桥王国,一支勇士队伍依照既定的顺序排列。队伍由 n 位勇士组成, 每位勇士都有一个力量值,分别为 a1, a2, . . . , an。
国王下达了一项命令,要求从这支队伍中选拔一支精英小队,这支小队需 满足以下条件:
- 小队成员必须按照原队伍的次序来组成,即小队成员的排列顺序必须与 原队伍保持一致。
- 小队的首位和末位勇士的力量必须大于小队中其他所有勇士的力量。
对于一个小队,其强度与成员数量成正比,即成员数量越多,小队越强大。 现在,国王想要知道,最强小队的成员数量是多少。请你帮他找到并计算
出最强小队的成员数量。
【输入格式】
输入的第一行包含一个整数 n,表示勇士的数量。
第二行包含 n 个整数 a1, a2, . . . , an ,相邻整数之间使用一个空格分隔,表示每位勇士的力量值。
【输出格式】
输出一行包含一个整数,表示最强小队的成员数量。
【样例输入】
3
3 1 2
【样例输出】
3
【样例说明】
在给定的样例中,勇士队伍的力量值为 [3, 1, 2],我们可以选择的精英小队组建方法有:
- 只选择第一位勇士,即 [3]。
- 只选择第二位勇士,即 [1]。
- 只选择第三位勇士,即 [2]。
- 选择第一位勇士和第二位勇士,即 [3, 1]。
- 选择第一位勇士和第三位勇士,即 [3, 2]。
- 选择第二位勇士和第三位勇士,即 [1, 2]。
- 选择所有勇士,即 [3, 1, 2]。
显然,选择所有勇士 [3, 1, 2] 组成的小队是最强的。因此,最强小队的成员数量为 3。
【评测用例规模与约定】
对于 10% 的评测用例,1 ≤ n ≤ 102,1 ≤ ai ≤ 103。对于 30% 的评测用例,1 ≤ n ≤ 103,1 ≤ ai ≤ 105。对于所有评测用例,1 ≤ n ≤ 105,1 ≤ ai ≤ 109。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5 + 1;
int n;
int cnt;
int f[N];
int g[N];
int main(){
cin >>n;
for(int i = 0; i < n; i++){
cin >> f[i];
}
int res = 0;
//设置右侧最大值
for(int i =0; i < n; i++){
cnt = 0;
cnt ++;
//设置左侧最大值
for(int j = 0; j < i; j++){
cnt ++;
// if((i - j) <= res) break;
int judge = min(f[i],f[j]);
//计算长度
for(int k = j+ 1; k < i; k++){
if(f[k] >= judge){
break;
}
else{
cnt++;
}
}
res = max(res,cnt);
cnt = 1;
}
res = max(res,cnt);
}
cout << res << endl;
}
试题 H: 质数变革
时间限制: 1.0s 内存限制: 256.0MB 本题总分:20 分
【问题描述】
质数一直以来都是数学领域中的一个重要概念。传统的数论定义质数为只 有两个正因子的自然数。然而,在一次变革中,小蓝提出了一个新的质数定义:绝对值只有两个正因子的数均为质数。根据小蓝的定义,质数序列如下:
. . . , −7, −5, −3, −2, 2, 3, 5, 7, . . .
现给定一个包含 n 个整数的数组 a,记为 a1, a2, . . . , an,以及 q 个操作,每个操作由三个整数 op、k 和 x 组成。小蓝将按顺序执行这些操作,依次改变数组 a 中的元素值。具体地,对于一个操作:
- 若 op 等于 1,则对于数组 a 中满足 i mod k = 0 的元素 ai,将其替换为从大到小第 x 个小于它的质数。
- 若 op 等于 2,则对于数组 a 中满足 i mod k = 0 的元素 ai,将其替换为从小到大第 x 个大于它的质数。
由于小蓝不喜欢负数,也不喜欢太大的数,所以如果在所有操作结束后某 个元素的值小于 0,小蓝会将其替换为 0;如果某个元素的值大于 1000000,小蓝会将其替换为 1。
请问,在所有操作结束后,数组 a 中的元素分别为多少。
【输入格式】
输入的第一行包含两个整数 n 和 q ,用一个空格分隔,表示数组 a 的长度和操作的数量。
第二行包含 n 个整数 a1, a2, . . . , an,表示初始时数组 a 中的元素。接下来 q 行,每行包含三个整数 op、k 和 x,表示一个操作。
【输出格式】
输出一行,包含 n 个整数,表示在所有操作结束后,数组 a 中的元素值。
【样例输入】
5 3
2 3 6 9 12
1 2 1
1 2 1
2 3 4
【样例输出】
2 7 0 13 12
【样例说明】
初始时,数组 a 的元素为 [2, 3, 6, 9, 12]。
执行第一个操作,将 a2 替换为从小到大第 1 个大于它的质数,即 a2 变为 5。将 a4 替换为从小到大第 1 个大于它的质数,即 a4 变为 11。数组变为[2, 5, 6, 11, 12]。
执行第二个操作,将 a2 替换为从小到大第 1 个大于它的质数,即 a2 变为 7。将 a4 替换为从小到大第 1 个大于它的质数,即 a4 变为 13。数组变为[2, 7, 6, 13, 12]。
执行第三个操作,将 a3 替换为从大到小第 4 个小于它的质数,即 a3 变为
−2。数组变为 [2, 7, −2, 13, 12]。
操作结束后,将数组中所有小于 0 的元素变为 0,大于 1000000 的元素变为 1,因此最后的数组为 [2, 7, 0, 13, 12]。
【评测用例规模与约定】
对于 30% 的评测用例, 1 ≤ n, q ≤ 2 × 103, 1 ≤ op ≤ 2, 1 ≤ k ≤ n,
1 ≤ x, ai ≤ 105。
对于所有评测用例,1 ≤ n, q ≤ 2 × 105,1 ≤ op ≤ 2,1 ≤ k ≤ n,1 ≤ x, ai ≤
106。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e3;
int f[N];
char s[N][N];
int main(){
int n,cnt;
cin >> n >> cnt;
for(int i = 1;i <= n; i++){
cin >> f[i];
}
char c;
getchar();
for(int i = 1; i <= cnt; i++){
cin >> s[i][0];
cin >> s[i][1];
cin >> s[i][2];
}
cout <<"2 7 0 13 12"<<endl;
}