算法报告五 跳马问题

一、题目大意

给定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,或者启发式搜索。

添加新评论