海龟制图是一个经典的题目,是要编写一个输入命令,让海龟按命令前进的程序 。
一、题目描述
在小学生中很流行的徽标语言使乌龟图形的概念声名远播。 想象一下,一只机械乌龟在C ++程序的控制下在房间里走动。 乌龟在上下两个位置之一中握住笔。当笔向下时,乌龟会在移动时追踪形状。 当笔在上面时,乌龟可以自由移动,而无需书写任何东西。 在此问题中,您将模拟乌龟的操作并创建一个计算机化的画板。请使用初始化为false的20 x 20阵列地板。 从包含它们的数组中读取命令。 始终跟踪乌龟的当前位置以及笔当前处于向上还是向下。 假定乌龟总是用笔向上从地板的位置(0,0)开始。 图中显示了程序必须处理的一组乌龟命令。
二、具体实现如下:
#include <iostream>
#include <iomanip>
using namespace std;
int matrix[100][100]={0},n=0,d=0,x=0,y=0;
void move(int);
void print();
int main()
{
int command;
while(cin>>command&&command!=0)
{
switch(command)
{
case 2:n=1;break; //摁下笔,行走有墨迹
case 1:n=0;break; //提起笔,行走无墨迹
case 3:move(3);break; //向右转
case 4:move(4);break; //向左转
case 5:move(5);break; //向前走
case 6:print();break;
default: cout<<"wrong!";
}
}
return 0;
}
void move(int command)
{
int a,b,dir[4][2]={0,1,1,0,0,-1,-1,0};
int t[8]={1,2,3,0,3,0,1,2};
char comma;
if(command!=5) //处理摁下3, 然后5,10的情况
cin>>a>>comma>>b;
else
cin>>comma>>b; //处理5,10的情况
if(command!=5)
d=t[command&4|d]; //解决方向问题
//具体的推导可以使用状态图转换,见下
else d=d;
for(int i=0;i<b;i++)
{
if(matrix[x][y]==0)
matrix[x][y]=n;
if(i<b-1)
{
x+=dir[d][0];
y+=dir[d][1];
}
}
}
void print()
{
for(int i=0;i<25;i++)
{
for(int j=0;j<50;j++)
{
if(matrix[i][j]==0)cout<<" ";
else cout<<" *";
}
cout<<endl;
}
}
关于这里的方向转换问题,可以用状态转换图来更好的归纳
首先我们先来定义方向:
注意这里x,y的情况跟我们熟悉的x是水平轴,y是垂直轴的情况不一致。这里x代表行,所以它的方向是垂直的。
然后画出状态转换图
画出状态转换图之后,其实我们可以用一大串的if else语句来实现方向转换功能,这里用数组把方向转换对存起来,效率会更高,该方法有些以空间换时间的意味。
正如上面所描述的意义,我们根据转换图先画出方向转换的关系,然后把这些关系以键值对的方式用map存起来,因为数组又可以理解成是键是下标、值是内容的map,所以可以用数组简化存储。
上面的图只举了向右转的例子,同理可得向左转的数组为{3,0,1,2}
if(command!=5)
d=t[command&4|d];
else d=d;
然后上面的程序就是使用了数组来实现方向转换,这里用位运算的方法使得两个转向数组合并为一个,因为:
3&4=000, 此时 d=t[command&4|d] 等价于 d = t[d],
4&4 = 100 ,此时 d=t[command&4|d] 等价于 d = t[d+4]。
如果不想这么复杂,你也可以用下面的方法, 可以达到同样的效果,几乎相同的效率。
int t1[4]={1,2,3,0};
int t2[4]={3,0,1,2};
if(command==3)
d=t1[d];
else if(command==4)
d=t2[d];
else
d=d; //command==5的情况,不用转向
另外,你会发现,x,y的增减也是用数组存了起来,因为它也是一个:方向->(delta_x, delta_y) 的映射。
三、总结
1.关于复杂的状态转换问题,我们可以把它存为一个键值对的映射,避免复杂的if else语句。
2.键为小范围整数的键值对映射,转换成数组效率更好。