算法报告五 跳马问题
16122020 钟顺源
一、题目大意
给定8*8方格棋盘,求棋盘上一只马从一个位置到达另一位置的最短路径长。
注意马是走“日”形的。
二、分析
这是一道显然的搜索题,用dfs或bfs均可,但bfs更加简单,广搜更加容易理解。
某一点的到起点的最短距离一定是从某一个合法位置跳到这个点的,从起点bfs,如果设当前的点是u,能到达的点是v,那么v到起点的最短距离=u到起点的最短距离+1(u到v的距离)。
那么对于一个u 有哪几个v呢?
v | v | |||
---|---|---|---|---|
v | v | |||
u | ||||
v | v | |||
v | v |
如上表,有八个方向,记录一下两维的偏移量。
使用一个vis数组,记录这个点访问过了没。
具体操作而言,从某起点出发,对每个点广度搜索,若这个点访问过了,则跳过这个点,否则搜索它可以到达的所有点,更新vis数组,更新到起点需要几步,入队列,重复操作,知道,队列为空。
这题基本就是这样做的,但数据量较少,我直接暴力枚举所有起点,求这个起点,到所有点的跳动次数。
三、代码
#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
bool vis[9][9];
int dis[9][9][9][9];
typedef pair<int,int> PII;
#define X first
#define Y second
#define clr(x,y) memset(x,0,sizeof(x) )
int dx[]={1,-1,1,-1,2,-2,2,-2};
int dy[]={2,2,-2,-2,1,1,-1,-1};
void bfs(int x,int y){
clr(vis,0);
clr(dis[x][y],INF);
dis[x][y][x][y]=0;
queue<PII> q;
q.push(PII(x,y));
while(!q.empty()){
PII cur=q.front();
q.pop();
int xx=cur.X,yy=cur.Y;
for(int i=0;i<8;i++){
int xxx=dx[i]+xx,yyy=dy[i]+yy;
if(xxx<0||xxx>8||yyy<0||yyy>8||vis[xxx][yyy]) continue;
vis[xxx][yyy]=1;
dis[x][y][xxx][yyy] = dis[x][y][xx][yy] + 1;
q.push(PII(xxx,yyy));
}
}
}
char from[5],to[5];
int main()
{
for(int i=1;i<=8;i++)
for(int j=1;j<=8;j++)
bfs(i,j);
while(~scanf("%s %s",from,to)){
int x=from[0]-'a'+1;
int y=from[1]-'0';
int xx=to[0]-'a'+1;
int yy=to[1]-'0';
printf("%s==>%s: %d moves\n",from,to,dis[x][y][xx][yy]);
}
return 0;
}
四、体会
这次是一个基础的bfs,与一些迷宫问题或是在方格上求最短距离不同,它有八个方向,然而本质是不变的,都是记录访问状态vis和离起点最短距离,当然全部暴力用bfs打表出来在数据量大的时候肯定不行,到时候可能要对一个个询问依次处理,要用到更高效的搜索,比如双向bfs,或者启发式搜索。