2021程序设计天梯赛在4月24日举办,本文是天梯赛的部分题解,有的问题在当时也没有得到满分,由于学校开启了天梯赛的重现比赛,再写一写。
注意:本文答案不是标准答案,每道题收获的分数写在了相应位置,注意查看。
有的人退出,有的人加入。始终如一,不忘初心。
L1-1 人与神 ( 5分 / 5 分) 第一个5分是代码获得的分数,第二个5分是题目总分,下同
跨界大神 L. Peter Deutsch 有一句名言:“To iterate is human, to recurse divine.”(迭代的是人,递归的是神)。本题就请你直接在屏幕上输出这句话。
输入格式:
本题没有输入。
输出格式:
在一行中输出
To iterate is human, to recurse divine.。
输入样例:
无
输出样例:
To iterate is human, to recurse divine.
【问题分析】签到题,直接输出即可。
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <cstring>
#include <sstream>
#include <stdlib.h>
#include <map>
#include <stack>
#include <queue>
#include <vector>
using namespace std;
int main(){
cout<<"To iterate is human, to recurse divine."<<endl;
system("pause");
return 0;
}
L1-2 两小时学完C语言 (5分 / 5 分)
知乎上有个宝宝问:“两个小时内如何学完 C 语言?”当然,问的是“学完”并不是“学会”。假设一本 C 语言教科书有 N 个字,这个宝宝每分钟能看 K 个字,看了 M 分钟。还剩多少字没有看?
输入格式
输入在一行中给出 3 个正整数,分别是 N(不超过 400 000),教科书的总字数;K(不超过 3 000),是宝宝每分钟能看的字数;M(不超过 120),是宝宝看书的分钟数。
题目保证宝宝看完的字数不超过 N。
输出格式:
在一行中输出宝宝还没有看的字数。
输入样例:
100000 1000 72
输出样例:
28000
【问题分析】大家千万不要像这位宝宝一样浮躁,简单的算数题,n-k*m即可;
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <cstring>
#include <sstream>
#include <stdlib.h>
#include <map>
#include <stack>
#include <queue>
#include <vector>
using namespace std;
typedef long long ll;
int main(){
int n,m,k;
cin>>n>>m>>k;
cout<<n-m*k<<endl;
return 0;
}
L1-3 强迫症 (10分 / 10 分)
小强在统计一个小区里居民的出生年月,但是发现大家填写的生日格式不统一,例如有的人写 199808,有的人只写 9808。有强迫症的小强请你写个程序,把所有人的出生年月都整理成 年年年年-月月 格式。对于那些只写了年份后两位的信息,我们默认小于 22 都是 20 开头的,其他都是 19 开头的。
输入格式:
输入在一行中给出一个出生年月,为一个 6 位或者 4 位数,题目保证是 1000 年 1 月到 2021 年 12 月之间的合法年月。
输出格式:
在一行中按标准格式 年年年年-月月 将输入的信息整理输出。
输入样例 1:
9808
输出样例 1:
1998-08
输入样例 2:
0510
输出样例 2:
2005-10
输入样例 3:
196711
输出样例 3:
1967-11
【问题分析】对于本题,可以看到输入可以分为两种格式:
- 年份全拼,字符串一共为6位;
- 年份不全拼,只写后两位,字符串一共为4位;而对于这种字符串,根据题目描述 我们默认小于
22
都是20
开头的,其他都是19
开头的又可以分为两种情况:
- 年份小于22的为21世纪年份;
- 其他的都为20世纪年份;
本题的考点为分支结构。
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <cstring>
#include <sstream>
#include <stdlib.h>
#include <map>
#include <stack>
#include <queue>
#include <vector>
using namespace std;
typedef long long ll;
int main(){
string s;
cin>>s;
if (s.size()==4){ //缩写
int year = (s.at(0)-'0')*10+(s.at(1)-'0'); //拿到前两位的值
if (year<22){
cout<<20;
} else{
cout<<19;
}
cout<<s.at(0)<<s.at(1)<<"-"<<s.at(2)<<s.at(3);
} else{ //详细
for (int i=0;i<s.size();i++){
cout<<s.at(i);
if (i==3){
cout<<"-";
}
}
}
return 0;
}
L1-4 降价提醒机器人 (10分 / 10 分)
小 T 想买一个玩具很久了,但价格有些高,他打算等便宜些再买。但天天盯着购物网站很麻烦,请你帮小 T 写一个降价提醒机器人,当玩具的当前价格比他设定的价格便宜时发出提醒。
输入格式:
输入第一行是两个正整数 N 和 M (1≤N≤100,0≤M≤1000),表示有 N 条价格记录,小 T 设置的价格为 M。
接下来 N 行,每行有一个实数 P**i(−1000.0<P**i<1000.0),表示一条价格记录。
输出格式:
对每一条比设定价格 M 便宜的价格记录 P
,在一行中输出 On Sale! P
,其中 P
输出到小数点后 1 位。
输入样例:
4 99
98.0
97.0
100.2
98.9
输出样例:
On Sale! 98.0
On Sale! 97.0
On Sale! 98.9
【问题分析】使用循环结构,对每一个价格纪录进行比较,发现更便宜 的提醒即可;
本题的考点为:循环结构
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <cstring>
#include <sstream>
#include <stdlib.h>
#include <map>
#include <stack>
#include <queue>
#include <vector>
using namespace std;
typedef long long ll;
int main(){
int n;
double m,p;
cin>>n>>m;
for (int i=0;i<n;i++){
cin>>p;
if (p<m){
printf("On Sale! %.1lf\n",p);
}
}
return 0;
}
L1-5 大笨钟的心情 (15分 / 15 分)
有网友问:未来还会有更多大笨钟题吗?笨钟回复说:看心情……本题就请你替大笨钟写一个程序,根据心情自动输出回答。
输入格式:
输入在一行中给出 24 个 [0, 100] 区间内的整数,依次代表大笨钟在一天 24 小时中,每个小时的心情指数。
随后若干行,每行给出一个 [0, 23] 之间的整数,代表网友询问笨钟这个问题的时间点。当出现非法的时间点时,表示输入结束,这个非法输入不要处理。题目保证至少有 1 次询问。
输出格式:
对每一次提问,如果当时笨钟的心情指数大于 50,就在一行中输出 心情指数 Yes
,否则输出 心情指数 No
。
输入样例:
80 75 60 50 20 20 20 20 55 62 66 51 42 33 47 58 67 52 41 20 35 49 50 63
17
7
3
15
-1
输出样例:
52 Yes
20 No
50 No
58 Yes
【问题分析】大笨钟是天梯赛的常客了,伴随了我天梯赛的三年~,今天终于知道大笨钟原来是姥姥的自称hhh。回归正题,这可能是这几年大笨钟系列最简单的一道题了,我们使用一个数组保存值,对给出的查询进行数组访问输出即可。
本题的考点是:数组
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <cstring>
#include <sstream>
#include <stdlib.h>
#include <map>
#include <stack>
#include <queue>
#include <vector>
using namespace std;
typedef long long ll;
int main()
{
int que, mood[24];
for (int i = 0; i < 24; i++)
{
cin >> mood[i];
}
while (1)
{
cin >> que;
if (que < 0 || que > 23)
{
break;
}
cout << mood[que] << " ";
if (mood[que] > 50)
{
cout << "Yes" << endl;
}
else
{
cout << "No" << endl;
}
}
return 0;
}
L1-6 吉老师的回归 (15分 / 15 分)
曾经在天梯赛大杀四方的吉老师决定回归天梯赛赛场啦!为了简化题目,我们不妨假设天梯赛的每道题目可以用一个不超过 500 的、只包括可打印符号的字符串描述出来,如:Problem A: Print "Hello world!"
。众所周知,吉老师的竞赛水平非常高超,你可以认为他每道题目都会做(事实上也是……)。因此,吉老师会按照顺序看题并做题。但吉老师水平太高了,所以签到题他就懒得做了(浪费时间),具体来说,假如题目的字符串里有 qiandao
或者 easy
(区分大小写)的话,吉老师看完这道题目不做。现在给定这次天梯赛总共有几道题目以及吉老师已经做完了几道题目,请你告诉大家吉老师现在正在做哪个题,或者吉老师已经把所有他打算做的题目做完了。
提醒:天梯赛有分数升级的规则,如果不做签到题可能导致团队总分不足以升级,一般的选手请千万不要学习吉老师的酷炫行为!
输入格式:
输入第一行是两个正整数 N,M (1≤M≤N≤30),表示本次天梯赛有 N 道题目,吉老师现在做完了 M 道。
接下来 N 行,每行是一个符合题目描述的字符串,表示天梯赛的题目内容。吉老师会按照给出的顺序看题——第一行就是吉老师看的第一道题,第二行就是第二道,以此类推。
输出格式:
在一行中输出吉老师当前正在做的题目对应的题面(即做完了 M 道题目后,吉老师正在做哪个题)。如果吉老师已经把所有他打算做的题目做完了,输出一行 Wo AK le
。
输入样例 1:
5 1
L1-1 is a qiandao problem.
L1-2 is so...easy.
L1-3 is Easy.
L1-4 is qianDao.
Wow, such L1-5, so easy.
输出样例 1:
L1-4 is qianDao.
输入样例 2:
5 4
L1-1 is a-qiandao problem.
L1-2 is so easy.
L1-3 is Easy.
L1-4 is qianDao.
Wow, such L1-5, so!!easy.
输出样例 2:
Wo AK le
【问题分析】字符串处理,主要的思路是给定一个值M代表已经做完的题目,而题目中如果出现了"qiandao"和"easy"的字眼就会被忽略(注意大小写敏感),因此我们只需要经过一个不包含上述两个子串的字符串就对m减一,直到m等于0或者AK(All Killed)了全部题目为止(这句话其实重复包含了m等于0且AK的情况,忽略细节~)。
本题的考点为:字符串处理;
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <cstring>
#include <sstream>
#include <stdlib.h>
#include <map>
#include <stack>
#include <queue>
#include <vector>
using namespace std;
typedef long long ll;
int main()
{
int n,m;
cin>>n>>m;
string in;
getchar();
for (int i=0;i<n;i++){
getline(cin,in);
if (in.find("qiandao")!=in.npos||in.find("easy")!=in.npos){
continue;
}
m--;
if (m<0){
cout<<in<<endl;
return 0;
}
}
cout<<"Wo AK le"<<endl;
return 0;
}
L1-7 天梯赛的善良 (20分 / 20 分)
天梯赛是个善良的比赛。善良的命题组希望将题目难度控制在一个范围内,使得每个参赛的学生都有能做出来的题目,并且最厉害的学生也要非常努力才有可能得到高分。于是命题组首先将编程能力划分成了 106 个等级(太疯狂了,这是假的),然后调查了每个参赛学生的编程能力。现在请你写个程序找出所有参赛学生的最小和最大能力值,给命题组作为出题的参考。
输入格式:
输入在第一行中给出一个正整数 N(≤2×104),即参赛学生的总数。随后一行给出 N 个不超过 106 的正整数,是参赛学生的能力值。
输出格式:
第一行输出所有参赛学生的最小能力值,以及具有这个能力值的学生人数。第二行输出所有参赛学生的最大能力值,以及具有这个能力值的学生人数。同行数字间以 1 个空格分隔,行首尾不得有多余空格。
输入样例:
10
86 75 233 888 666 75 886 888 75 666
输出样例:
75 3
888 2
【问题分析】找到数列中最小最大值并且计数,数据规模为10^4,因此可以直接排序,对于计数,我常用的方法有数组计数(优点:方便快捷;缺点:比较消耗空间,尤其是数据较多时)和结构体计数(优点:节省空间;缺点:需要写cmp参数,使用不方便),此处使用数组计数。
本题考点为排序**、计数器
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <string.h>
#include <cstring>
#include <sstream>
#include <stdlib.h>
#include <map>
#include <stack>
#include <queue>
#include <vector>
using namespace std;
typedef long long ll;
int Count[1000001];
int main()
{
int n,s[20001];
cin>>n;
for (int i=0;i<n;i++){
cin>>s[i];
Count[s[i]]++;
}
sort(s,s+n);
cout<<s[0]<<" "<<Count[s[0]]<<endl;
cout<<s[n-1]<<" "<<Count[s[n-1]]<<endl;
return 0;
}
L1-8 乘法口诀数列 (20分 / 20 分)
本题要求你从任意给定的两个 1 位数字 a1 和 a2 开始,用乘法口诀生成一个数列 {a**n},规则为从 a1 开始顺次进行,每次将当前数字与后面一个数字相乘,将结果贴在数列末尾。如果结果不是 1 位数,则其每一位都应成为数列的一项。
输入格式:
输入在一行中给出 3 个整数,依次为 a1、a2 和 n,满足 0≤a1,a2≤9,0<n≤103。
输出格式:
在一行中输出数列的前 n 项。数字间以 1 个空格分隔,行首尾不得有多余空格。
输入样例:
2 3 10
输出样例:
2 3 6 1 8 6 8 4 8 4
样例解释:
数列前 2 项为 2 和 3。从 2 开始,因为 2×3=6,所以第 3 项是 6。因为 3×6=18,所以第 4、5 项分别是 1、8。依次类推…… 最后因为第 6 项有 6×8=48,对应第 10、11 项应该是 4、8。而因为只要求输出前 10 项,所以在输出 4 后结束。
【问题分析】本题需要仔细读题,现在将题目简化:
- 数列的每一个元素是前两个元素的积;
- 此次的积为2位的话(不可能出现3位及以上,最大9*9=81),将十位和个位拆开作为新的两位;
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <algorithm>
#include <string.h>
#include <cstring>
#include <sstream>
#include <stdlib.h>
#include <map>
#include <stack>
#include <queue>
#include <vector>
using namespace std;
typedef long long ll;
int Count[1000001];
int main()
{
int a1, a2, n;
int content[1005], index = 3; //index是指content指针,当index==n时就需要输出了,初始为2是因为已经有a1和a2了
int now = 2; //now是准备进行运算的第二个数字
cin >> a1 >> a2 >> n;
content[1] = a1;
content[2] = a2;
if (n == 1)
{
cout << a1<<endl;
return 0;
}
else if (n == 2)
{
cout << a1<<" "<<a2 << endl;
return 0;
}
cout << a1 << " " << a2;
stringstream ss;
while (1)
{
ss.clear();
ss.str("");
int tmp = content[now] * content[now - 1];
ss << tmp;
if (ss.str().size() == 1)
{
content[index++] = tmp;
cout << " " <<tmp;
if (index - 1 == n)
{
return 0;
}
}
else
{
content[index++] = ss.str().at(0)-'0';
cout<<" "<<content[index-1];
if (index - 1 == n)
{
system("pause");
return 0;
}
content[index++] = ss.str().at(1)-'0';
cout<<" "<<content[index-1];
if (index - 1 == n)
{
return 0;
}
}
now++;
}
return 0;
}
L2-1 包装机 (25分 / 25 分)
一种自动包装机的结构如图 1 所示。首先机器中有 N 条轨道,放置了一些物品。轨道下面有一个筐。当某条轨道的按钮被按下时,活塞向左推动,将轨道尽头的一件物品推落筐中。当 0 号按钮被按下时,机械手将抓取筐顶部的一件物品,放到流水线上。图 2 显示了顺序按下按钮 3、2、3、0、1、2、0 后包装机的状态。
一种特殊情况是,因为筐的容量是有限的,当筐已经满了,但仍然有某条轨道的按钮被按下时,系统应强制启动 0 号键,先从筐里抓出一件物品,再将对应轨道的物品推落。此外,如果轨道已经空了,再按对应的按钮不会发生任何事;同样的,如果筐是空的,按 0 号按钮也不会发生任何事。
现给定一系列按钮操作,请你依次列出流水线上的物品。
输入格式:
输入第一行给出 3 个正整数 N(≤100)、M(≤1000)和 Sma**x(≤100),分别为轨道的条数(于是轨道从 1 到 N 编号)、每条轨道初始放置的物品数量、以及筐的最大容量。随后 N 行,每行给出 M 个英文大写字母,表示每条轨道的初始物品摆放。
最后一行给出一系列数字,顺序对应被按下的按钮编号,直到 −1 标志输入结束,这个数字不要处理。数字间以空格分隔。题目保证至少会取出一件物品放在流水线上。
输出格式:
在一行中顺序输出流水线上的物品,不得有任何空格。
输入样例:
3 4 4
GPLT
PATA
OMSA
3 2 3 0 1 2 0 2 2 0 -1
输出样例:
MATA
【问题分析】这是一道利用了简单数据结构的模拟题,对于给定的N条轨道和流水线,分别是一个队列,筐是一个栈,这是根据他们的特性判断的;主要考察从实际中抽象并应用数据结构解决问题的能力,熟练使用STL可以为解决此问题提供大大的便利;
下面将题目简化:
- 对于每一个轨道(队列),按下他们对应的轨道号时,就将队头的元素出队,并且弹出筐(栈)中;
- 按下0时,将筐(栈)顶 的元素放到流水线(队列)尾部;
- 需要特别注意两种情况:
- 筐(栈)满之后,在下一次触发轨道->筐的操作时需要先强制触发"0"操作,即筐->流水线的操作;
- 在筐没有货物时,按下"0"不会发生任何事情;
- 当轨道没有货物时,按下取货不会发生任何事情;
本题考点:队列、栈,STL
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <cstring>
#include <sstream>
#include <stdlib.h>
#include <map>
#include <stack>
#include <queue>
#include <vector>
using namespace std;
typedef long long ll;
int main()
{
int n, m, Max;
cin >> n >> m >> Max;
vector<queue<char> > tracks(101); //轨道
stack<char> backet; //筐
queue<char> sl; //streamline 流水线
char k;
for (int i = 1; i <= n; i++)
{
getchar();
for (int j = 0; j < m; j++)
{
cin >> k;
tracks[i].push(k);
}
}
int op;
while (1)
{
cin>>op;
if (op==-1){
break;
}
if (op==0){ //筐->流水线(栈->队列)
if(!backet.empty()){ //筐为空不反应
sl.push(backet.top());
backet.pop();
}
} else{ //轨道->筐(队列->栈)
if (!tracks[op].empty()){ //轨道为空不反应
if (backet.size()==Max){ //筐此刻满了
sl.push(backet.top());
backet.pop();
backet.push(tracks[op].front());
tracks[op].pop();
} else{
backet.push(tracks[op].front());
tracks[op].pop();
}
}
}
}
while (!sl.empty()){
cout<<sl.front();
sl.pop();
}
return 0;
}
L2-2 病毒溯源 (25 分)
病毒容易发生变异。某种病毒可以通过突变产生若干变异的毒株,而这些变异的病毒又可能被诱发突变产生第二代变异,如此继续不断变化。现给定一些病毒之间的变异关系,要求你找出其中最长的一条变异链。在此假设给出的变异都是由突变引起的,不考虑复杂的基因重组变异问题 —— 即每一种病毒都是由唯一的一种病毒突变而来,并且不存在循环变异的情况。
输入格式:
输入在第一行中给出一个正整数 N(≤104),即病毒种类的总数。于是我们将所有病毒从 0 到 N−1 进行编号。
随后 N 行,每行按以下格式描述一种病毒的变异情况:
k 变异株1 …… 变异株k
其中 k
是该病毒产生的变异毒株的种类数,后面跟着每种变异株的编号。第 i 行对应编号为 i 的病毒(0≤i<N)。题目保证病毒源头有且仅有一个。
输出格式:
首先输出从源头开始最长变异链的长度。
在第二行中输出从源头开始最长的一条变异链,编号间以 1 个空格分隔,行首尾不得有多余空格。如果最长链不唯一,则输出最小序列。
注:我们称序列 { a1,⋯,a**n } 比序列 { b1,⋯,b**n } “小”,如果存在 1≤k≤n 满足 a**i=b**i 对所有 i<k 成立,且 a**k<b**k。
输入样例:
10
3 6 4 8
0
0
0
2 5 9
0
1 7
1 2
0
2 3 1
输出样例:
4
0 4 9 1
【问题分析】这道题在第一眼看到的时候以为要使用并查集,实际上这是一道树的求最长路径的题,
突然收到计算机大赛的任务,五一后再补(挖坑)。