骑士的移动(Knight Moves, Uva439)- BFS模板题

题目

  • 参考网址:https://vjudge.net/problem/UVA-439
  • 大意:
    按照国际象棋🐴的移动规则,求🐴最小需要多少步从起点跳到终点。(老BFS模板题了~)

知识点

  • BFS求最短路径

思路

BFS模板题,输入转换为坐标r (row),c (column)行和列,使用队列的方式进行BFS,使用vis记录是否访问,使用dis记录距离(当然两个可以合并为一个来写)。

  • 每次pop出队首u检查是否是目的位置,是的话返回当前节点位置所记录下的dis。
  • 否则,向“四面八方”扩散出去,判断条件为即不超出边界,且没有访问过 (is_ok(r1, c1) && !vis[r1][c1])。(访问过再走一边相当于绕圈子了,肯定不是最短路径)如果满足条件,将该状态所在的位置的dis更新为dis[u] + 1(距离+1),vis[当前位置]打上标记然后入队,每次循环检查队列是否为空,如果队列为空,仍然没有找到目的位置,则说明无解(当然这道题是不可能有无解的)。
  • 状态的记录建议用个结构体,我用了数组记录,debug了半天,问题在后续有记录!!

代码

# include <iostream>
# include <queue>

using namespace std;

string alphabet = "abcdefg";  // 用于映射 0-7
int end_r, end_c;  // 结束坐标
int dr[] = {1, 2, 2, 1, -1, -2, -2, -1};  // 顺时针
int dc[] = {-2, -1, 1, 2, 2, 1, -1, -2};
int vis[8][8], dis[8][8];  // 记录访问 & 距离
queue<int *> q;  // 状态以status[1][1]的方式存入

bool is_ok(int r, int c) {  // 检查是否超出边界
    return r >= 0 && r < 8 && c >= 0 && c < 8;
}

int bfs(int r, int c) {
    int status[] = {r, c};
    q.push(status);
    vis[r][c] = 1; // 初始起点标记为访问过
    dis[r][c] = 0; // 起始点距离为0
    while (!q.empty()) {  // 队列不为空
        int *u = q.front();  q.pop();  // pop出队头
		
		// 判断是否已经是目的位置,是的话返回结果即可
        if (u[0] == end_r && u[1] == end_c)
            return dis[u[0]][u[1]];

        for (int i = 0; i < 8; i++) {
            int r1 = u[0] + dr[i];
            int c1 = u[1] + dc[i];
            if (is_ok(r1, c1) && !vis[r1][c1]) {  
            	// 可以移动且没有访问过(访问过的绕回来再走没有意义,徒增dis)
                int *temp = new int[2]{r1, c1};  
                // !这里不能直接 int temp[2] = {r1, c1}; 不然每次地址都一样会被覆盖掉
                vis[r1][c1] = 1;  // 标记为“已经访问过”
                dis[r1][c1] = dis[u[0]][u[1]] + 1;  // 距离更新
                q.push(temp);  // 入队	
            }
        }
    }
}

int main() {
    // 输入 & 初始化
    char input[4];
    cin >> input[0] >> input[1];
    cin >> input[2] >> input[3];
    int start_r = input[1] - 49;
    int start_c = find(alphabet.begin(), alphabet.end(), input[0]) - alphabet.begin();  	// 映射为 0-7
    end_r = input[3] - 49;
    end_c = find(alphabet.begin(), alphabet.end(), input[2]) - alphabet.begin();  // 映射为 0-7
    memset(vis, 0, sizeof(int[8][8]));  // 初始化
    memset(dis, -1, sizeof(int[8][8]));

    // bfs得到最小路径
    int ans = bfs(start_r, start_c);
    cout << "To get from " << alphabet[start_c] <<start_r + 1 << " to "
         << alphabet[start_c] << end_r + 1 << " takes " << ans << " knight moves." << endl;

    return 0;
}

过程中遇到的问题 & 解决

  • BFS求最短路径没什么好说,唯一一个调了半天的bug是 int *temp = new int[2]{r1, c1}; 这一句,不能直接 int temp[2] = {r1, c1}; 。不然的话会覆盖上一个地址,有点百思不得其解这个编译时候的逻辑QAQ。

测试

输入:

e2 e4
a1 b2
b2 c3
a1 h8
a1 h7
h8 a1
b1 c3
f6 f6

结果:✔️

To get from e2 to e4 takes 2 knight moves.
To get from a1 to b2 takes 4 knight moves.
To get from b2 to c3 takes 2 knight moves.
To get from a1 to h8 takes 6 knight moves.
To get from a1 to h7 takes 5 knight moves.
To get from h8 to a1 takes 6 knight moves.
To get from b1 to c3 takes 1 knight moves.
To get from f6 to f6 takes 0 knight moves.
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值