华南师范大学软件学院蓝桥杯预选赛于 2017 年 12 月 5 日成功举行,共有 145 名同学有效参赛(提交过代码),112 人 AC 1 题以上,其中 1 人成功AK,2 人涉嫌抄袭代码被hack。
前言
这是 BGX 第一次发题解,如有不周到之处望各位看官原谅。
//轻点喷轻点喷,我背,我背还不行嘛(逃
此外,这是一篇非官方题解,如有言论不得当视为 BGX 一人之过。
题解
A.字母图形
Description
利用字母可以组成一些美丽的图形,下面给出了一个例子:
ABCDEFG
BABCDEF
CBABCDE
DCBABCD
EDCBABC
这是一个
5
行
Input
输入一行,包含两个整数
n
和
Output
输出
n
行,每行
Sample Input
5 7
Sample Output
ABCDEFG
BABCDEF
CBABCDE
DCBABCD
EDCBABC
题目大意
对应第
i
行输出第
参考思路
双重循环进行矩阵构造,以行数确立外层循环,内层循环顺序输出对应字符。
参考代码
#include <stdio.h>
int n,m;
int main(void)
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n;i++)
{
for (int j=i;j>=2 && i-j<m;--j)
putchar('A'+j-1);
for (int j=1;j<=m-i+1;++j)
putchar('A'+j-1);
puts("");
}
return 0;
}
易错点解析
- 图形规律没有找准。一部分同学对于当
n<m
时应当如何处理没有搞明白,认为此时第
n+1
行应当与第
1
行相同。虽然这个问题与题面表述不清有关,但是在尝试提交答案错误后应当仔细思考规律是否有误,这种情况下找到正确规律应该不难。
/反正是蓝桥杯原题,题意有问题绝不背锅/(逃 - 对题意理解有误。输入中表明 (
0≤n,m≤26 )是 n 与m 两个数同时大于等于 0 且小于等于 26,而不是只有 n≤0 和 m≤26 。 - 对字符串操作不熟练。一部分同学的想法是将每一行都写入字符数组中以字符串方式输出,然而对字符串在数组中的表示方式不熟练,数组大小只开到 26,导致当 m==26 时因字符串结束符 \0 无地可容而出现运行错误。
- 对循环操作特别是循环进行(终止)条件没有思考清楚。当 m>n 时第二层循环应及时停止,有一部分同学没有考虑到这个情况,导致第 n 行后的输出都多出了一些字符。
- 还有少部分输出了非字母字符的,运用字符数组不当导致数组越界的,或者滥用 goto 语句的,进入死循环不跳出的,诸如此类问题,不一一讨论了。
B.字符串对比
Description
给定两个仅由大写字母或小写字母组成的字符串(长度介于
1:两个字符串长度不等。比如 Beijing 和 Hebei
2:两个字符串不仅长度相等,而且相应位置上的字符完全一致(区分大小写)。比如 Beijing 和 Beijing
3:两个字符串长度相等,相应位置上的字符仅在不区分大小写的前提下才能达到完全一致(也就是说,它并不满足情况2)。比如 beijing 和 BEIjing
4:两个字符串长度相等,但是即使是不区分大小写也不能使这两个字符串一致。比如 Beijing 和 Nanjing
编程判断输入的两个字符串之间的关系属于这四类中的哪一类,给出所属的类的编号。
Input
包括两行,每行都是一个字符串。
Output
仅有一个数字,表明这两个字符串的关系编号。
Sample Input
BEIjing
beiJing
Sample Output
3
题目大意
对应不同的字符串对,按照规则进行分类讨论并输出。
参考思路
读入字符串并根据规则进行分类讨论,并注意分类讨论的顺序和种类判断方式。
参考代码
#include <stdio.h>
#include <string.h>
#include <ctype.h>
char a[20],b[20];
int x=1,y=1;
int main(void)
{
scanf("%s%s",a,b);
if (strlen(a)!=strlen(b))
{
printf("1\n");
return 0;
}
int len=strlen(a);
for (int i=0;i<len;++i)
{
if (a[i]!=b[i])
x=0;
if (tolower(a[i])!=tolower(b[i]))
y=0;
}
if (!y)
printf("4\n");
else if (!x)
printf("3\n");
else
printf("2\n");
//printf(!y?"4\n":!x?"3\n":"2\n");
return 0;
}
易错点解析
- 不清楚如何计算字符串长度。strlen() 或者 sizeof() 函数如果不记得的话还可以使用遍历字符串直到遇到 \0 的方法,然而仍然有不少同学在这一环节吃亏。
- 字符串输入方式不熟悉。有同学尝试逐个读入字符直到遇到换行符 \n 的方式进行输入,然而并不清楚使用 scanf() 会跳过换行符,导致运行错误。
- 分类讨论判断方式有误。部分同学对每个规则对应的判断方式模棱两可,导致该输出 3 的时候输出 4 或其它数字等问题。
- else 语句运用不当。部分代码出现同时输出两个数字的问题,大部分都是只有 if 没有 else 或 else 与 if 不对称造成的。
- ctype 库函数运用不当。有部分同学使用 ctype 里的库函数进行判断时将 islower()/isupper() 当成了 tolower()/toupper(),导致答案错误。
- 本题数据较弱导致部分同学只把数组开到 20 也可以 AC,请各位重视字符串结束符 \0。
C.TIMEWORTH牛
Description
一头名叫TIMEWORTH的牛逃跑到了森林里。农夫
cgy
开始用他的专家技术追捕这头牛。你的任务是模拟他们的行为(牛和
cgy
)。
追击在
10∗10
的平面网格内进行。一个格子可以是:空地,障碍物, 牛, 或者农民
cgy
。
牛和
cgy
可以在同一个格子内(当他们相遇时),但是他们都不能进入有障碍的格子。
一个格子可以是:
. 空地
* 障碍物
C 牛
F
cgy
这里有一个地图的例子:
∗...∗.....
......∗...
...∗...∗..
..........
...∗.F....
∗.....∗...
...∗......
..C......∗
...∗.∗....
.∗.∗......
牛在地图里以固定的方式游荡。每分钟,它可以向前移动或是转弯。如果前方无障碍(地图边沿也是障碍),它会按照原来的方向前进一步。否则它会用这一分钟顺时针转
90∘
。同时,它不会离开地图。
cgy
深知牛的移动方法,他也这么移动。(对,
cgy
就是这么傻)
每次(每分钟)
cgy
和牛的移动是同时的。如果他们在移动的时候穿过对方,但是没有在同一格相遇,我们不认为他们相遇了。当他们在某分钟末在某格子相遇,那么追捕结束。
读入
10
行表示
cgy
,牛和所有障碍的位置的地图。每行都只包含
10
个字符,表示的含义和上面所说的相同,可以确定地图中只有一个 ‘F’ 和一个 ‘C’.’F’ 和 ‘C’ 一开始不会处于同一个格子中。
计算农夫
cgy
需要多少分钟来抓住他的牛,假设牛和
cgy
一开始的行动方向都是正北(即上)。如果
cgy
和牛永远不会相遇,输出
0
。
Input
输入包括
Output
输出一个数字,表示 cgy 需要多少时间才能抓住牛。如果 cgy 无法抓住牛,则输出 0 。
Sample Input
......∗...
...∗...∗..
..........
...∗.F....
∗.....∗...
...∗......
..C......∗
...∗.∗....
.∗.∗......
Sample Output
49
Hint
你可以认为如果在很长一段时间(比如 500000 分钟)之内牛和 cgy 都没有相遇的话,那么之后也不会出现这种情况。
题目大意
求两个物体按照指定运动规则在图上运动能否相遇,并输出运动时间。
参考思路
分别对两个物体模拟其运动,判断某时刻时两物体坐标是否相同,相同则输出累计时间,累计时间超过一定数目时判断为进入死循环,即不可相遇。
读入地图后遍历找出两个物体所在位置,最后循环时间模拟运动过程从而得到答案,当然循环时要注意数组边界条件。
参考代码
先放出AK选手的代码,虽然不是很好懂,但是模拟过程写得比较精妙
# include <stdio.h>
int d[4][2] = {{0,-1},{1,0},{0,1},{-1,0}};
int main()
{
char s[12][12];
int i,xc,yc,xf,yf,tc = 0,tf = 0,t = 1,j;
for(i = 0;i < 10;i++)
scanf("%s",s[i]);
for(i = 0;i < 10;i++)
for(j = 0;j < 10;j++)
{
if(s[i][j] == 'C')
{
yc = i;
xc = j;
}
if(s[i][j] == 'F')
{
yf = i;
xf = j;
}
}
while(t < 500000)
{
if(s[yc + d[tc % 4][1]][xc + d[tc % 4][0]] == '*' ||
xc + d[tc % 4][0] == -1 ||
xc + d[tc % 4][0] == 10 ||
yc + d[tc % 4][1] == -1 ||
yc + d[tc % 4][1] == 10)
tc++;
else
{
yc += d[tc % 4][1];
xc += d[tc % 4][0];
}
if(s[yf + d[tf % 4][1]][xf + d[tf % 4][0]] == '*' ||
xf + d[tf % 4][0] == -1 ||
xf + d[tf % 4][0] == 10 ||
yf + d[tf % 4][1] == -1 ||
yf + d[tf % 4][1] == 10)
tf++;
else
{
yf += d[tf % 4][1];
xf += d[tf % 4][0];
}
if(xc == xf&&yc == yf) {break;}
t++;
}
printf("%d\n",t != 500000?t:0);
return 0;
}
本来这道题是没有最后的提示的,为了降低难度加上了 500000 的限度。如果没有提示,除了自行构造一个比较大但不至于超时的数字进行时间检测之外,还可以记录下每个时间点的状态,当某个时间点出现与之前的时间点相同的状态时,则视为进入死循环,这样就可以在 500000 分钟前检测到死循环,提高程序运行效率。
同时,可以通过提前将边界设置为障碍物的方式,简化模拟运动过程中的判断条件,提高程序的可读性和容错率。
#include <stdio.h>
char g[15][15];
int xf,yf,xc,yc,dirf,dirc,cnt;
int vist[15][15][15][15][5][5];//记录状态的六维数组
int main(void)
{
//读入地图
for (int i=1;i<=10;++i)
scanf("%s",g[i]+1);
//遍历地图找到两个物体所在位置
for (int i=1;i<=10;++i)
for (int j=1;j<=10;++j)
{
if (g[i][j]=='F')
{
xf=i;
yf=j;
g[i][j]='.';
}
if (g[i][j]=='C')
{
xc=i;
yc=j;
g[i][j]='.';
}
}
//将边界设为障碍物,可简化模拟中的判断
for (int i=0;i<=10;++i)
g[0][i]='*';
for (int i=0;i<=10;++i)
g[11][i]='*';
for (int i=0;i<=10;++i)
g[i][0]='*';
for (int i=0;i<=10;++i)
g[i][11]='*';
//模拟运动过程
while (xf!=xc || yf!=yc)//若两个物体还没相遇则进行运动
{
++cnt;//计时器自增
//若前方不是障碍物则针对不同方向前进,否则改变方向
//dir? 变量表示物体的方向,0↑,1→,2↓,3←,可通过取模进行循环
if (dirf==0 && g[xf-1][yf]!='*')
--xf;
else if (dirf==1 && g[xf][yf+1]!='*')
++yf;
else if (dirf==2 && g[xf+1][yf]!='*')
++xf;
else if (dirf==3 && g[xf][yf-1]!='*')
--yf;
else
dirf=(dirf+1)%4;
if (dirc==0 && g[xc-1][yc]!='*')
--xc;
else if (dirc==1 && g[xc][yc+1]!='*')
++yc;
else if (dirc==2 && g[xc+1][yc]!='*')
++xc;
else if (dirc==3 && g[xc][yc-1]!='*')
--yc;
else
dirc=(dirc+1)%4;
//状态记录,若之前没出现过此状态则记录,若出现过就是已进入死循环
if (!vist[xf][yf][xc][yc][dirf][dirc])
vist[xf][yf][xc][yc][dirf][dirc]=1;
else
{
printf("0\n");
return 0;
}
}
printf("%d\n",cnt);
return 0;
}
易错点解析
- 循环跳出判断条件出错。比赛过后为几位险些 AC 的同学叹息,时间超限的几乎都是循环条件写错,导致无法跳出循环,时间超限。
- 居然有将 B 题的代码交到 C 题来的操作,真是令人智熄。
- 若判断行进条件为下一格是否为空地,则必须将原来两个物体所在位置设为空地,否则当物体到相应位置后视该格为障碍导致答案错误。
- AK爷真强居然上面两条都没犯错。
统计数据
题号 | 题目 | AC 率(正确/提交) |
---|---|---|
A | 字母图形 | 30.06%(98/326) |
B | 字符串对比 | 50.00%(80/160) |
C | TIMEWORTH牛 | 11.11%(1/9) |
总结
瞎搞的总结。我又不是官方。
不会归纳,随便说两句咯233。
A 和 B 两题梯度不够,虽然再难一点好像就崩了,AK爷也是最后 10min 左右才一血了 C 题,要是 B 再占用 10min 的时间可能心态就崩了。
C 确实要比上一年的要难,但是有了 500000 的 Hint 之后貌似简单了不少,至少推理证明上就直接少了一大步,初步估计会有 5 人左右做出来,不过没想到都被循环条件坑惨了,剩下一位勇士 AK 到最后。
整体来说个人认为这次比赛很能代表软院新生的 coding 水平啦,而且我觉得应该是相当有代表性了,起码比上一年的人型自走判题机判题法以及自动自觉数据检测法更加公平公正了。
并且今年新生的平均水平相比去年来说是要高的,尽管题目难度整体提升了,今年的有效答题率比上一年也就少那么了一点,这也证明了我院 C 语言教学水平确实有所提高。
仍然证明了我院存在一些问题,其中比较重要的就是 “贫富差距悬殊” ,与上一年一样有人 AK 有人签到失败,虽然社会就是这么险恶的。//逃
然后比较看不过去的就是缩进问题,相当一部分代码缩进极其紊乱甚至毫无缩进,可读性体验太差了,看 bug 的时候都快看哭了T_T。不说依照 K & R 或者 ANSI 标准吧,起码 Tab 是不能一个都没的吧,还有一些左一行右一行的代码漂移选手,你当这是秋名山呢。
分享一个当时去现场玩的时候的小故事,让一位同学收起书本不收,反而回了一句 “书都不给看那还打什么代码啊” 。嗯,故事说完了。
最后,AK爷好强啊。
(本文若有言论不得当,为作者一人之过)
(本文纯属瞎搞,一个字也不要信,包括代码)
(如有雷同纯属抄袭,这个锅我背,我背好吧)
//行了行了能不能别演了这儿可是 CSDN!!!