1450:【例 3】Knight Moves
时间限制: 1000 ms 内存限制: 65536 KB
提交数: 1170 通过数: 406
【题目描述】
原题来自:POJ 1915
编写一个程序,计算一个骑士从棋盘上的一个格子到另一个格子所需的最小步数。骑士一步可以移动到的位置由下图给出。
【输入】
第一行给出骑士的数量 n。
在接下来的 3n 行中,每 3 行描述了一个骑士。其中,
第一行一个整数 L 表示棋盘的大小,整个棋盘大小为 L×L;
第二行和第三行分别包含一对整数 (x,y),表示骑士的起始点和终点。假设对于每一个骑士,起始点和终点均合理。
【输出】
对每一个骑士,输出一行一个整数表示需要移动的最小步数。如果起始点和终点相同,则输出 0。
【输入样例】
3
8
0 0
7 0
100
0 0
30 50
10
1 1
1 1
【输出样例】
5
28
0
【提示】
对于 100% 的数据,有 4≤L≤300,保证 0≤x,y≤L−1。
思路:
这道题可以用双向宽度搜索优化给定了起始状态和结束状态,求最少步数,显然是用BFS,为了节省时间,选择双向BFS。双向BFS,即从起点向终点搜,从终点向起点搜,扩展各自的状态,直到出现两者扩展的状态重合优化:每次选择结点少的扩展。
```#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdlib>
using namespace std;
struct node
{
int x,y;
}q[2][100001]; //定义两个队列
int text,ans,n,l[2],r[2];
int dis[2][301][301],v[2][301][301];
int dx[8]={-2,-2,-1,-1,1,1,2,2}; //位移
int dy[8]={-1,1,-2,2,-2,2,-1,1};
int expand(int k) //对队列 k 进行扩展
{
int t,i,j,x,y,d,tx,ty;
x=q[k][l[k]].x;
y=q[k][l[k]].y;
d=dis[k][x][y];
for(int i=0;i<8;i++) //八个方向扩展
{
tx=x+dx[i]; //新点
ty=y+dy[i];
if(tx>=0&&tx<=n&&ty>=0&&ty<=n&&!v[k][tx][ty]) //合法而且没走过
{
v[k][tx][ty]=1; //标记走过
r[k]++; //入队
q[k][r[k]].x=tx;
q[k][r[k]].y=ty;
dis[k][tx][ty]=d+1; //记录步数
if(v[1-k][tx][ty])
//判断另一个队列中是否已经走过这个点,也就是判断是否重合相遇
//如果相遇,就找到了一条完整的最短路径
//k=0时,1-k=1
//k=1时,1-k=0
{
ans=dis[k][tx][ty]+dis[1-k][tx][ty];
return 1;
}
}
}
return 0;
}
void bfs()
{
if(q[0][1].x==q[1][1].x&&q[0][1].y==q[1][1].y) //起点终点本就相同
{
ans=0; return;
}
v[0][q[0][1].x][q[0][1].y]=1; //标记走过
v[1][q[1][1].x][q[1][1].y]=1;
l[0]=r[0]=1; //初始化头指针尾指针
l[1]=r[1]=1;
while(l[0]<=r[0]&&l[1]<=r[1]) //两个队列都非空,先扩展结点数少的
{
if(r[0]-l[0]<r[1]-l[1])
{
if(expand(0)) return; //找到答案啦
l[0]++; // 没找到,移动头指针继续找
}
if(r[0]-l[0]>=r[1]-l[1])
{
if(expand(1)) return;
l[1]++;
}
}
}
int main()
{
scanf("%d",&text);
for(int i=1;i<=text;i++) //多组数据
{
memset(dis,0,sizeof(dis));
memset(v,0,sizeof(v));
memset(q,0,sizeof(q));
scanf("%d",&n); n=n-1;
scanf("%d%d",&q[0][1].x,&q[0][1].y); //起点
scanf("%d%d",&q[1][1].x,&q[1][1].y); //终点
bfs();
printf("%d\n",ans);
}
return 0;
}