1043.跳马
时限:1000ms 内存限制:10000K 总时限:3000ms
描述
在国际象棋中,马的走法与中车象棋类似,即俗话说的“马走日”,下图所示即国际象棋中马(K)在一步能到达的格子(其中黑色的格子是能到达的位置)。
7 | 6 | |||
8 | 5 | |||
K | ||||
1 | 4 | |||
2 | 3 |
现有一200*200大小的国际象棋棋盘,棋盘中仅有一个马,给定马的当前位置(S)和目标位置(T),求出马最少需要多少跳才能从当前位置到达目标位置。
输入
本题包含多个测例。输入数据的第一行有一个整数N(1<=N<=1000),表示测例的个数,接下来的每一行有四个以空格分隔的整数,分别表示马当前位置及目标位置的横、纵坐标C(x,y)和G(x,y)。坐标由1开始。
输出
对于每个测例,在单独的一行内输出一个整数,即马从当前位置跳到目标位置最少的跳数。
#include <iostream>
#include <queue>
using namespace std;
queue <int> q1;
int used[40001]; //该格子是否被访问过
int step[40001]; //走到每一格所用步数
int n;
int cnt; //步数
int cx,cy,gx,gy; //从0开始
void init(); //初始化
int bfs(); //广搜
int moveto(int m, int d); //返回格子m去方向d跳到的的格子序号
//如果该格不能走,返回-1
//序号从0开始
int main()
{
cin>>n;
for(int i=0; i<n; i++)
{
cin>>cx>>cy>>gx>>gy;
init(); //初始化
cnt=bfs(); //广搜
cout<<cnt<<endl;
}
return 0;
}
void init() //初始化
{
for(int i=0; i<40001; i++) //初始化各数组
{
step[i]=used[i]=0;
}
while(!q1.empty()) //清空队列
{
q1.pop();
}
cnt=0;
int x=(cx-1)*200+cy-1; //起始格子的序号
step[x]=0;
used[x]=1;
q1.push(x);
}
int bfs() //广搜
{
while(!q1.empty())
{
int top=q1.front();
//cout<<top/200<<' '<<top%200<<endl;
q1.pop();
for(int i=1; i<9; i++) //八个方向
{
int y=moveto(top, i);
if(y>=0) //如果可以走
{
if(y==(gx-1)*200+gy-1)
{
return step[top]+1;
}
else
{
used[y]=1;
step[y]=step[top]+1;
q1.push(y);
}
}
}
}
return -1;
}
//不可走情况:已经被访问过/出界
int moveto(int m, int d)
{
int x=m/200;
int y=m%200;
int next;
switch(d)
{
case 1:
{
if(x+1<200&&y-2>=0) //判断出界
{
next=(x+1)*200+(y-2);
if(used[next]==0) //判断是否访问过
{
return next;
}
}
break;
}
case 2:
{
if(x+2<200&&y-1>=0) //判断出界
{
next=(x+2)*200+(y-1);
if(used[next]==0)
{
return next;
}
}
break;
}
case 3:
{
if(x+2<200&&y+1<200) //判断出界
{
next=(x+2)*200+(y+1);
if(used[next]==0)
{
return next;
}
}
break;
}
case 4:
{
if(x+1<200&&y+2<200) //判断出界
{
next=(x+1)*200+(y+2);
if(used[next]==0)
{
return next;
}
}
break;
}
case 5:
{
if(x-1>=0&&y+2<200) //判断出界
{
next=(x-1)*200+(y+2);
if(used[next]==0)
{
return next;
}
}
break;
}
case 6:
{
if(x-2>=0&&y+1<200) //判断出界
{
next=(x-2)*200+(y+1);
if(used[next]==0)
{
return next;
}
}
break;
}
case 7:
{
if(x-2>=0&&y-1>=0) //判断出界
{
next=(x-2)*200+(y-1);
if(used[next]==0)
{
return next;
}
}
break;
}
case 8:
{
if(x-1>=0&&y-2>=0) //判断出界
{
next=(x-1)*200+(y-2);
if(used[next]==0)
{
return next;
}
}
break;
}
}
return -1;
}
【后记】
1.写的第一个广搜,吭哧了一上午,主要卡在:
(1)坐标从[1, 1]开始,想当然的把格子也从1开始编号,结果坐标[200, 200]变成[40000]号又变成坐标[201, 0],出了bug,然后还是用回熟悉的从[0, 0]坐标开始,从0号开始编号;
(2)初始化忘记清空队列
2.用了一个moveto函数,当可以moveto的时候返回的是将要入队的格子序号(>=0),如果判断不能moveto,返回-1