题目描述#
一个机器人会接收行动的指令,请绘制它的移动轨迹。 使用
_
表示横线,|
表示竖线,要求图形的每行行尾无多余的空格。注意!!! 如果同一格有横竖两种轨迹,画竖线。
指令格式为
d n
,表示按方向d
移动n步。方向为LRUD
,依次为左右上下。比如依次执行
R 2, U 3, L 5, D 7, R 11
五条指令时,轨迹图如下:_____ | | | | | __| | | | |___________输入格式#
第一行是一个整数n(1≤n≤10000),表示指令的条数。 以后的n行,每行一条指令。
输出格式#
按格式要求输出轨迹图。
题目保证轨迹图的宽与高不会超过600。
样例输入#
4 L 1 U 1 R 1 D 1样例输出#
_ |_|
解题思路:这题可能是今年OJ中Top 5的题了,因为它的轨迹行走,图形打印都有很多的限定条件,而题目也没有仔细说明,全要靠我们自己去看样例或者试错去挖掘。这题难点就在这儿了。 下面我们来看看有哪些需要避坑和注意的点。
后面想了下,说了这么多,画了这么几个模糊的图,还不如让你们运行下面的代码,去看看是怎么输出的。(/流汗)
1. 题目保证 “轨迹图的宽与高不会超过600”,但轨迹怎么走是无法预测的,所以要申请一个保证能存下轨迹图的数组—— maps[1210][1210], 起点从 stx = 601,sty = 601 开始,这样上下左右都留了 600个位置。
2. 因为起点在数组中心位置,所以不可能再从 第1行,第1列 一直输出到 第1210行,第1210列。因此我们需要定义几个标记变量——ups,downs,lefts,分别记录轨迹图的 上下左 边界。 另外题目又要求:每行行尾无多余的空格。 所以每行的右边界 需要单独记录。 tags[1210] 数组的作用就是记录每一行的右边界。 tags[603] = 610,表示第603行,第610列之后就不用再输出了。
3. 每一步指令执行结束后,轨迹点停留的坐标(stx,sty) 在最后一步的下一格。比如28行:for (int i = 0; i < n; i ++, sty --),在向左已经走完了之后,最后还有执行 sty -- (左移一格) ~~~~ 而边框坐标,定为 最后一步所在格子的坐标。 比如 向左走肯定要判定是否更新左边界:if (sty < lefts) lefts = sty+1; 向下走了要判定是否更新下边界:if (stx > downs) downs = stx-1;
如图: 各个方向,最后坐标点都停留在该方向的下一格(紫色符号),但是边框还是记录的最后一个符号的位置。
4. 当前指令和上一个指令方向相反时。符号输入不再从 “下一格”开始输入了,而是从 “最后一步” 所在的位置原路返回。
5. 当前指令和上一个指令方向不相反时。#1 当 上一个指令是 向左/右 ,@1 当前指令为向右/左或向上时,直接从当前坐标的开始 打印;@2 当前指令为向下时,需要额外把sty向下移一格。#2当 上一个指令是 向下 ,当前指令为向下时,不需要额外处理, @3 当前指令为 向左/右 时,stx要上移一格,然后sty 向左/右 移一格。 #3 当 上一个指令是 向上 ,当前指令为向上时,不需要额外处理,@4 当前指令为 向左/右 时,sty 向左/右 移一格。
具体对照代码中 *1、*2、*3 注释
6. 因为第3点,我们定义的边框是每个指令走的最后一步的坐标,但如果像 在一个向左指令结束完之后,后面接了一个向上/下的指令,如上图的@1 @2,那么左边界应该再向左移一格,所以在每个指令之中,还需要判断一下(其他情况同理),详情对比代码注释 !A! 和 !B! / !C! 向下是个特别的情况,不需要再额外考虑。
结语:考虑完上述几个情况,本题也就差不多出来了(内容写的有点多,逻辑上可能有点乱,等我慢慢改吧)怎么说呢,大模拟题,别人的代码你能看懂就看看,看不下去的直接CV吧。在看完题解之后,还是要在脑中 把n步指令一次性 完全且无差错地模拟出来才行。模拟题要自己理清之后才能算是自己的。
AC代码:
#include <stdio.h>
int N,n;
char d,p;
int ups,downs,lefts,stx,sty;
int tags[1210]; // 右边框坐标
char maps[1210][1210];
void drawmaps()
{
p = 'V';
stx = 601,sty = 601; // stx为行,sty为列
ups = 601,downs = 601,lefts = 601; // 上/下/左 边框极限坐标,
for (int i = 0; i < 1210; i ++) // 所有元素先默认都为 ' '(空格)
for (int j = 0; j < 1210; j ++)
maps[i][j] = ' ';
}
void draw(char d,int n)
{
if (d == 'L')
{
if (p == 'R') sty --; // *1;如果上一步是向右,这步要先往左退一格
if (p == 'D') stx --; // *2;如果上一步是向下,这步应该在同一行左移
if (p == 'U' || p == 'D') sty --; // *3;如果上一步是向上/下,这步起始位置要往左移一步
if (stx < ups) ups = stx; // !B! 如果比ups更高,要重新更新。
if (tags[stx] < sty) tags[stx] = sty;
for (int i = 0; i < n; i ++, sty --) // 如果这点已经有'|'了,就不能打印'_' '|'优先级更高
if (maps[stx][sty] != '|')
maps[stx][sty] = '_';
if (sty < lefts) lefts = sty+1; // !A! 默认最后的'_'是当前轨迹图最左边的的符号
}
else if (d == 'R')
{
if (p == 'L') sty ++; // *1;如果上一步是向左,这步要先往右退一格
if (p == 'D') stx --; // *2
if (p == 'U' || p == 'D') sty ++; // *3
if (stx < ups) ups = stx; // !B!
for (int i = 0; i < n; i ++, sty ++)
if (maps[stx][sty] != '|')
maps[stx][sty] = '_';
if (tags[stx] < sty) tags[stx] = sty-1;
}
else if (d == 'U')
{
if (p == 'D') stx --; // *1;如果上一步是向下,这步开始前要往上退一格
if (sty < lefts) lefts = sty; // !A!(32行) 如果'|'的位置比lefts还小,要重新更新
for (int i = 0; i < n; i ++, stx --)
{
maps[stx][sty] = '|';
if ( tags[stx] < sty)
tags[stx] = sty;
}
if (stx < ups) ups = stx+1; // !B! 默认最后的'|' 所在位置是轨迹图最高位。
}
else if (d == 'D')
{
if (p == 'U') stx ++; // *1;如果上一步是向上,这步开始前要先往下退一格
if (p == 'L' || p == 'R') stx ++; // *3;如果上一步是左/右走, 这步要从下一行开始
if (sty < lefts) lefts = sty; // !A!(同理)
for (int i = 0; i < n; i ++, stx ++)
{
maps[stx][sty] = '|';
if ( tags[stx] < sty)
tags[stx] = sty;
}
if (stx > downs) downs = stx-1; // !C! 同理
}
}
void output()
{
for (int i = ups; i <= downs; i ++)
{
for (int j = lefts; j <= tags[i]; j ++)
printf("%c",maps[i][j]);
puts("");
}
}
int main()
{
drawmaps(); // 初始化数组
scanf("%d",&N);
for (int i = 1; i <= N; i ++)
{
scanf(" %c %d",&d,&n); // 读入指令,记得%c之前留个 空格
draw(d,n);
p = d; // 记录上一个指令
}
output();
return 0;
}