2020团队程学设计天梯赛-总决赛
L1-6 吃火锅(15分)
题目描述:
这种天气你有什么破事打电话给我基本没用。但是如果你说“吃火锅”,那就厉害了,我们的故事就开始了。
本题要求你实现一个程序,自动检查你朋友给你发来的信息里有没有 chi1 huo3 guo1
。
输入格式:
输入每行给出一句不超过 80 个字符的、以回车结尾的朋友信息,信息为非空字符串,仅包括字母、数字、空格、可见的半角标点符号。当读到某一行只有一个英文句点 .
时,输入结束,此行不算在朋友信息里。
输出格式:
首先在一行中输出朋友信息的总条数。然后对朋友的每一行信息,检查其中是否包含 chi1 huo3 guo1
,并且统计这样厉害的信息有多少条。在第二行中首先输出第一次出现 chi1 huo3 guo1
的信息是第几条(从 1 开始计数),然后输出这类信息的总条数,其间以一个空格分隔。题目保证输出的所有数字不超过 100。
如果朋友从头到尾都没提 chi1 huo3 guo1
这个关键词,则在第二行输出一个表情 -_-#
。
输入样例 1:
Hello!
are you there?
wantta chi1 huo3 guo1?
that's so li hai le
our story begins from chi1 huo3 guo1 le
.
输出样例 1:
5
3 2
输入样例 2:
Hello!
are you there?
wantta qi huo3 guo1 chi1huo3guo1?
that's so li hai le
our story begins from ci1 huo4 guo2 le
.
输出样例 2:
5
-_-#
思路:使用string函数中的find()函数查找即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h>
2 using namespace std;
3 int main(){
4 string s="chi1 huo3 guo1";
5 string str;
6 int sum = 0;
7 int t,flag = 0,num = 0;
8 while(1){
9 getline(cin,str);
10 if(str==".") break; ///荖坑人了。。。一开始写的str[0]='.' ,一个测试点硬是不过
11 sum++;
12 if(str.find(s) != string::npos){
13 if(!flag){
14 flag = 1;
15 t = sum;
16 num++;
17 }
18 else num++;
19 }
20 }
21 cout << sum << endl;
22 if(!num) cout << "-_-#" << endl;
23 else cout << t << " " << num << endl;
24 return 0;
25 }
L1-7 前世档案(20分)
题目描述:
网络世界中时常会遇到这类滑稽的算命小程序,实现原理很简单,随便设计几个问题,根据玩家对每个问题的回答选择一条判断树中的路径(如下图所示),结论就是路径终点对应的那个结点。
现在我们把结论从左到右顺序编号,编号从 1 开始。这里假设回答都是简单的“是”或“否”,又假设回答“是”对应向左的路径,回答“否”对应向右的路径。给定玩家的一系列回答,请你返回其得到的结论的编号。
输入格式:
输入第一行给出两个正整数:N(≤)为玩家做一次测试要回答的问题数量;M(≤)为玩家人数。
随后 M 行,每行顺次给出玩家的 N 个回答。这里用 y
代表“是”,用 n
代表“否”。
输出格式:
对每个玩家,在一行中输出其对应的结论的编号。
输入样例:
3 4
yny
nyy
nyn
yyn
输出样例:
3
5
6
2
思路:把玩家的回答看成一串二进制数。(y为0,n为1,最后再加上1【2的0次方】)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h>
2 using namespace std;
3 int main(){
4 int n,m;
5 char c;
6 cin>>n>>m;
7 while(m--){
8 int sum=0;
9 for(int i=0;i<n;i++){
10 cin>>c;
11 sum*=2;
12 if(c=='n')sum+=1;
13 }
14 cout<<sum+1<<endl;
15 }
16 return 0;
17 }
L1-8 刮刮彩票(20分)
题目描述:
“刮刮彩票”是一款网络游戏里面的一个小游戏。如图所示:
每次游戏玩家会拿到一张彩票,上面会有 9 个数字,分别为数字 1 到数字 9,数字各不重复,并以 3 的“九宫格”形式排布在彩票上。
在游戏开始时能看见一个位置上的数字,其他位置上的数字均不可见。你可以选择三个位置的数字刮开,这样玩家就能看见四个位置上的数字了。最后玩家再从 3 横、3 竖、2 斜共 8 个方向中挑选一个方向,方向上三个数字的和可根据下列表格进行兑奖,获得对应数额的金币。
数字合计 | 获得金币 | 数字合计 | 获得金币 |
---|---|---|---|
6 | 10,000 | 16 | 72 |
7 | 36 | 17 | 180 |
8 | 720 | 18 | 119 |
9 | 360 | 19 | 36 |
10 | 80 | 20 | 306 |
11 | 252 | 21 | 1,080 |
12 | 108 | 22 | 144 |
13 | 72 | 23 | 1,800 |
14 | 54 | 24 | 3,600 |
15 | 180 |
现在请你写出一个模拟程序,模拟玩家的游戏过程。
输入格式:
输入第一部分给出一张合法的彩票,即用 3 行 3 列给出 0 至 9 的数字。0 表示的是这个位置上的数字初始时就能看见了,而不是彩票上的数字为 0。
第二部给出玩家刮开的三个位置,分为三行,每行按格式 x y
给出玩家刮开的位置的行号和列号(题目中定义左上角的位置为第 1 行、第 1 列。)。数据保证玩家不会重复刮开已刮开的数字。
最后一部分给出玩家选择的方向,即一个整数: 1 至 3 表示选择横向的第一行、第二行、第三行,4 至 6 表示纵向的第一列、第二列、第三列,7、8分别表示左上到右下的主对角线和右上到左下的副对角线。
输出格式:
对于每一个刮开的操作,在一行中输出玩家能看到的数字。最后对于选择的方向,在一行中输出玩家获得的金币数量。
输入样例:
1 2 3
4 5 6
7 8 0
1 1
2 2
2 3
7
输出样例:
1 5 6 180
思路:模拟。
一开始,想的过复杂了,加了一项筛选该位置是否被刮开,后来想想好像没必要。玩家肯定会往最有利于自己的情况挑选方向。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h>
2 using namespace std;
3 int s[25]={0,0,0,0,0,0,10000,36,720,360,80,252,108,72,54,180,72,180,119,36,306,1080,144,1800,3600};
4 int g[5][5];
5 int vis[15];
6 int x,y,t,n,m;
7 int main(){
8 int sum = 0;
9 for(int i = 1;i <= 3;i++){
10 for(int j = 1;j <= 3;j++){
11 cin >> t;
12 if(!t){
13 x = i;
14 y = j;
15 }
16 g[i][j] = t;
17 vis[t] = 1;
18 }
19 }
20 for(int i = 1;i < 10;i++){
21 if(!vis[i]){
22 g[x][y] = i;
23 break;
24 }
25 }
26 for(int i = 1;i <= 3;i++){
27 cin >> x >> y;
28 cout << g[x][y] << endl;
29 }
30 cin>>t;
31 if(t == 1){
32 for(int i = 1;i <= 3;i++)
33 sum += g[1][i];
34 }
35 if(t == 2){
36 for(int i = 1;i <= 3;i++)
37 sum += g[2][i];
38 }
39 if(t == 3){
40 for(int i = 1;i <= 3;i++)
41 sum += g[3][i];
42 }
43 if(t == 4){
44 for(int i = 1;i <= 3;i++)
45 sum += g[i][1];
46 }
47 if(t == 5){
48 for(int i = 1;i <= 3;i++)
49 sum += g[i][2];
50 }
51 if(t == 6){
52 for(int i = 1;i <= 3;i++)
53 sum += g[i][3];
54 }
55 if(t == 7){
56 for(int i = 1;i <= 3;i++)
57 sum += g[i][i];
58 }
59 if(t == 8){
60 for(int i = 1;i <= 3;i++)
61 sum += g[i][4-i];
62 }
63 cout << s[sum] << endl;
64 return 0;
65 }
L2-1 简单计算器 (25分)
题目描述:
本题要求你为初学数据结构的小伙伴设计一款简单的利用堆栈执行的计算器。如上图所示,计算器由两个堆栈组成,一个堆栈 S1 存放数字,另一个堆栈 S2 存放运算符。计算器的最下方有一个等号键,每次按下这个键,计算器就执行以下操作:
- 从 S1 中弹出两个数字,顺序为 n1 和 n2;
- 从 S2 中弹出一个运算符 op;
- 执行计算 n2 op n1;
- 将得到的结果压回 S1。
直到两个堆栈都为空时,计算结束,最后的结果将显示在屏幕上。
输入格式:
输入首先在第一行给出正整数 N(1),为 S1 中数字的个数。
第二行给出 N 个绝对值不超过 100 的整数;第三行给出 N−1 个运算符 —— 这里仅考虑 +
、-
、*
、/
这四种运算。一行中的数字和符号都以空格分隔。
输出格式:
将输入的数字和运算符按给定顺序分别压入堆栈 S1 和 S2,将执行计算的最后结果输出。注意所有的计算都只取结果的整数部分。题目保证计算的中间和最后结果的绝对值都不超过 1。
如果执行除法时出现分母为零的非法操作,则在一行中输出:ERROR: X/0
,其中 X
是当时的分子。然后结束程序。
输入样例 1:
5
40 5 8 3 2
/ * - +
输出样例 1:
2
输入样例 2:
5
2 5 8 4 4
* / - +
输出样例 2:
ERROR: 5/0
思路:栈的操作,使用C++ STL的stack。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 int main(){ 4 stack<int>s1; 5 stack<char>s2; 6 int n,x; 7 int n1,n2; 8 char c,s; 9 cin >> n; 10 for(int i = 0;i < n;i++){ 11 cin >> x; 12 s1.push(x); 13 } 14 for(int i = 0;i < n - 1;i++){ 15 cin >> c; 16 s2.push(c); 17 } 18 int t; 19 while(1){ 20 if(s1.empty()){ 21 cout << t << endl; 22 break; 23 } 24 if(s2.empty()) { 25 cout << t << endl; 26 break; 27 } 28 n1 = s1.top(); 29 s1.pop(); 30 n2 = s1.top(); 31 s1.pop(); 32 s = s2.top(); 33 s2.pop(); 34 if(s == '+'){ 35 t = n2 + n1; 36 s1.push(t); 37 } 38 if(s == '-'){ 39 t = n2 - n1; 40 s1.push(t); 41 } 42 if(s == '*'){ 43 t = n2 * n1; 44 s1.push(t); 45 } 46 if(s == '/'){ 47 if(n1){ 48 t = n2 / n1; 49 s1.push(t); 50 } 51 else { 52 printf("ERROR: %d/0\n",n2); 53 break; 54 } 55 } 56 } 57 return 0; 58 }
L2-2 口罩发放 (25分)
题目描述:
为了抗击来势汹汹的 COVID19 新型冠状病毒,全国各地均启动了各项措施控制疫情发展,其中一个重要的环节是口罩的发放。
某市出于给市民发放口罩的需要,推出了一款小程序让市民填写信息,方便工作的开展。小程序收集了各种信息,包括市民的姓名、身份证、身体情况、提交时间等,但因为数据量太大,需要根据一定规则进行筛选和处理,请你编写程序,按照给定规则输出口罩的寄送名单。
输入格式:
输入第一行是两个正整数 D 和 P(1),表示有 D 天的数据,市民两次获得口罩的时间至少需要间隔 P 天。
接下来 D 块数据,每块给出一天的申请信息。第 i 块数据(,)的第一行是两个整数 Ti 和 Si(1),表示在第 i 天有 Ti 条申请,总共有 Si 个口罩发放名额。随后 Ti 行,每行给出一条申请信息,格式如下:
姓名 身份证号 身体情况 提交时间
给定数据约束如下:
姓名
是一个长度不超过 10 的不包含空格的非空字符串;身份证号
是一个长度不超过 20 的非空字符串;身体情况
是 0 或者 1,0 表示自觉良好,1 表示有相关症状;提交时间
是 hh:mm,为24小时时间(由00:00
到23:59
。例如 09:08。)。注意,给定的记录的提交时间不一定有序;身份证号
各不相同,同一个身份证号被认为是同一个人,数据保证同一个身份证号姓名是相同的。
能发放口罩的记录要求如下:
身份证号
必须是 18 位的数字(可以包含前导0);- 同一个身份证号若在第 i 天申请成功,则接下来的 P 天不能再次申请。也就是说,若第 i 天申请成功,则等到第 i+P+1 天才能再次申请;
- 在上面两条都符合的情况下,按照提交时间的先后顺序发放,直至全部记录处理完毕或 Si 个名额用完。如果提交时间相同,则按照在列表中出现的先后顺序决定。
输出格式:
对于每一天的申请记录,每行输出一位得到口罩的人的姓名及身份证号,用一个空格隔开。顺序按照发放顺序确定。
在输出完发放记录后,你还需要输出有合法记录的、身体状况为 1 的申请人的姓名及身份证号,用空格隔开。顺序按照申请记录中出现的顺序确定,同一个人只需要输出一次。
输入样例:
4 2
5 3
A 123456789012345670 1 13:58
B 123456789012345671 0 13:58
C 12345678901234567 0 13:22
D 123456789012345672 0 03:24
C 123456789012345673 0 13:59
4 3
A 123456789012345670 1 13:58
E 123456789012345674 0 13:59
C 123456789012345673 0 13:59
F F 0 14:00
1 3
E 123456789012345674 1 13:58
1 1
A 123456789012345670 0 14:11
输出样例:
D 123456789012345672
A 123456789012345670
B 123456789012345671
E 123456789012345674
C 123456789012345673
A 123456789012345670
A 123456789012345670
E 123456789012345674
样例解释:
输出中,第一行到第三行是第一天的部分;第四、五行是第二天的部分;第三天没有符合要求的市民;第六行是第四天的部分。最后两行按照出现顺序输出了可能存在身体不适的人员。
思路:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 struct Node{ 4 string name; ///姓名 5 string id; ///身份证 6 int flag; ///健康状况 7 int hh,mm; ///时间 8 int t; ///时间 秒 9 int idx; ///顺序 10 }; 11 int cmp(Node a,Node b){ ///排序条件 12 if(a.t!=b.t) return a.t<b.t; 13 else return a.idx<b.idx; 14 } 15 int Judge(string s){///身份证格式判断 16 if(s.size()!=18) return 0; 17 for(int i=0;i<s.size();i++){ 18 if(!isdigit(s[i])) return 0; ///isdigit函数判断字符串是否全由数字构成 19 } 20 return 1; 21 } 22 int main(){ 23 int d,p,t,s,cnt=0; 24 Node data[10005],data1[10005]; 25 map<string,int> M; 26 map<string,int> vis; 27 cin>>d>>p; 28 for(int i=1;i<=d;i++){ 29 cin>>t>>s; 30 for(int j=1;j<=t;j++){ 31 cin>>data[j].name>>data[j].id>>data[j].flag; 32 scanf("%d:%d",&data[j].hh,&data[j].mm); 33 data[j].t=data[j].hh*60+data[j].mm; ///转化为:秒 34 data[j].idx=j; ///记录顺序 35 36 if(M.find(data[j].id)==M.end()) M[data[j].id]=0; 37 if(data[j].flag==1 && Judge(data[j].id) && vis.find(data[j].id)==vis.end()){ 38 vis[data[j].id]=0; 39 data1[cnt++]=data[j]; 40 } 41 } 42 sort(data+1,data+t+1,cmp); 43 for(int j=1,q=0;j<=t && q<s;j++){//发放口罩 44 if(Judge(data[j].id)&&(!M[data[j].id]||i-M[data[j].id]>p)){//未发放过的或者已发放过过了p天的 45 cout<<data[j].name<<" "<<data[j].id<<endl; 46 q++; 47 M[data[j].id]=i; 48 } 49 } 50 } 51 for(int i=0;i<cnt;i++) 52 cout<<data1[i].name<<" "<<data1[i].id<<endl; 53 return 0; 54 }
L2-3 完全二叉树的层序遍历 (25分)
题目描述:
一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是完美二叉树。对于深度为 D 的,有 N 个结点的二叉树,若其结点对应于相同深度完美二叉树的层序遍历的前 N 个结点,这样的树就是完全二叉树。
给定一棵完全二叉树的后序遍历,请你给出这棵树的层序遍历结果。
输入格式:
输入在第一行中给出正整数 N(≤),即树中结点个数。第二行给出后序遍历序列,为 N 个不超过 100 的正整数。同一行中所有数字都以空格分隔。
输出格式:
在一行中输出该树的层序遍历序列。所有数字都以 1 个空格分隔,行首尾不得有多余空格。
输入样例:
8
91 71 2 34 10 15 55 18
输出样例:
18 34 55 71 2 10 15 91
思路:模拟二叉树的后序遍历(假设A,B是完全二叉树,题目给了A树的后序遍历序列(只知道序列,不能直观的知道对应节点编号的数据),B先看成是空树,然后用B树去模拟后序遍历过程,在过程中填入A树的后序遍历序列数据,A树的形状以及对应节点编号的数据就在B树上得到了还原,还原的B树按节点编号从1到n输出数据就是层序遍历的结果)。用数组表示B树,根节点编号为1,递归模拟后序遍历。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 int tree[35],T[35]; 4 int n,pos = 1; 5 void dfs(int x){ 6 if(x > n) return; 7 dfs(2*x);//左子树 8 dfs(2*x+1);//右子树 9 tree[x]=T[pos++]; 10 } 11 int main(){ 12 cin >> n; 13 for(int i = 1;i <= n;i++) cin >> T[i]; 14 dfs(1); 15 for(int i = 1;i <= n;i++){ 16 i==n? printf("%d\n",tree[i]):printf("%d ",tree[i]); 17 } 18 return 0; 19 }
L2-4 网红点打卡攻略 (25分)
题目描述:
一个旅游景点,如果被带火了的话,就被称为“网红点”。大家来网红点游玩,俗称“打卡”。在各个网红点打卡的快(省)乐(钱)方法称为“攻略”。你的任务就是从一大堆攻略中,找出那个能在每个网红点打卡仅一次、并且路上花费最少的攻略。
输入格式:
首先第一行给出两个正整数:网红点的个数 N(1)和网红点之间通路的条数 M。随后 M 行,每行给出有通路的两个网红点、以及这条路上的旅行花费(为正整数),格式为“网红点1 网红点2 费用”,其中网红点从 1 到 N 编号;同时也给出你家到某些网红点的花费,格式相同,其中你家的编号固定为 0
。
再下一行给出一个正整数 K,是待检验的攻略的数量。随后 K 行,每行给出一条待检攻略,格式为:
n V1 V2 ⋯ Vn
其中 ( 是攻略中的网红点数,Vi 是路径上的网红点编号。这里假设你从家里出发,从 V1 开始打卡,最后从 Vn 回家。
输出格式:
在第一行输出满足要求的攻略的个数。
在第二行中,首先输出那个能在每个网红点打卡仅一次、并且路上花费最少的攻略的序号(从 1 开始),然后输出这个攻略的总路费,其间以一个空格分隔。如果这样的攻略不唯一,则输出序号最小的那个。
题目保证至少存在一个有效攻略,并且总路费不超过 1。
输入样例:
6 13
0 5 2
6 2 2
6 0 1
3 4 2
1 5 2
2 5 1
3 1 1
4 1 2
1 6 1
6 3 2
1 2 1
4 5 3
2 0 2
7
6 5 1 4 3 6 2
6 5 2 1 6 3 4
8 6 2 1 6 3 4 5 2
3 2 1 5
6 6 1 3 4 5 2
7 6 2 1 3 4 5 2
6 5 2 1 4 3 6
输出样例:
3
5 11
样例说明:
第 2、3、4、6 条都不满足攻略的基本要求,即不能做到从家里出发,在每个网红点打卡仅一次,且能回到家里。所以满足条件的攻略有 3 条。
第 1 条攻略的总路费是:(0->5) 2 + (5->1) 2 + (1->4) 2 + (4->3) 2 + (3->6) 2 + (6->2) 2 + (2->0) 2 = 14;
第 5 条攻略的总路费同理可算得:1 + 1 + 1 + 2 + 3 + 1 + 2 = 11,是一条更省钱的攻略;
第 7 条攻略的总路费同理可算得:2 + 1 + 1 + 2 + 2 + 2 + 1 = 11,与第 5 条花费相同,但序号较大,所以不输出。
思路:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define inf 0x3f3f3f3f 4 const int MAX = 205; 5 int g[MAX][MAX],a[MAX]; 6 int N,M,n,K; 7 int x,y,z; 8 int sum; 9 int vis[MAX]; 10 int main(){ 11 cin >> N >> M; 12 memset(g,-1,sizeof g); 13 for(int i = 0;i < M;i++){ 14 cin >> x >> y >> z; 15 g[x][y] = z; 16 g[y][x] = z; 17 } 18 cin >> K; 19 int minn = inf; 20 int pos; 21 for(int j = 1;j <= K;j++){ 22 memset(vis,0,sizeof vis); 23 cin >> n; 24 int flag = 1; 25 for(int i = 1;i <= n;i++){ 26 cin >> a[i]; 27 vis[a[i]]=1; 28 } 29 a[0]=a[n+1]=0; 30 if(n > N) continue; 31 for(int i = 1;i <= n;i++){ 32 if(!vis[i]){ 33 flag = 0; 34 break; 35 } 36 } 37 if(!flag) continue; 38 int t = 0; 39 for(int i = 0;i <= n;i++){ 40 if(g[a[i]][a[i+1]] > 0){ 41 t += g[a[i]][a[i+1]]; //花费 42 }else { 43 flag = 0; 44 break; 45 } 46 } 47 if(!flag) continue; 48 sum++; 49 if(t < minn){ 50 minn = t; 51 pos = j; 52 } 53 } 54 cout << sum << endl; 55 cout << pos << " " << minn << endl; 56 return 0; 57 }