32 骑士的最短路线(Knight Shortest Path)

1 题目

题目:骑士的最短路线(Knight Shortest Path)
描述:给定骑士在棋盘上的 初始 位置(一个2进制矩阵 0 表示空 1 表示有障碍物),找到到达 终点 的最短路线,返回路线的长度。如果骑士不能到达则返回 -1 。

  • 起点跟终点必定为空.

  • 骑士不能碰到障碍物.

  • 路径长度指骑士走的步数.

  • 如果骑士的位置为 (x,y),他下一步可以到达以下这些位置:

    (x + 1, y + 2)
    (x + 1, y - 2)
    (x - 1, y + 2)
    (x - 1, y - 2)
    (x + 2, y + 1)
    (x + 2, y - 1)
    (x - 2, y + 1)
    (x - 2, y - 1)
    

lintcode题号——611,难度——medium

样例1:

输入:
[[0,0,0],
 [0,0,0],
 [0,0,0]]
source = [2, 0] destination = [2, 2]
输出: 2
解释:
[2,0]->[0,1]->[2,2]

样例2:

输入:
[[0,1,0],
 [0,0,1],
 [0,0,0]]
source = [2, 0] destination = [2, 2]
输出:-1

2 解决方案

2.1 思路

  找最短路径,使用宽度优先搜索,由于要记录步数,所以搜索过程中需要按层搜索,每层即是一步,每次宽搜都找到下一步能够到达的位置,需要对已经走过的位置进行记录,防止一直绕圈。

2.2 时间复杂度

  图上的宽度优先搜索,算法的时间复杂度为O(n+m),其中n为节点数,m为边数。R行C列的矩阵,可以看成有R*C个节点和R*C*2条边的图,在其上进行BFS,时间复杂度为O(R*C)

2.3 空间复杂度

  图上的宽度优先搜索,使用了queue队列数据结构,队列最大容量为节点数,即R*C,算法的空间复杂度为O(R*C)

3 源码

细节:

  1. 使用宽度优先搜索。
  2. 由于要记录步数,所以需要进行层级遍历,进入循环时记录queue的大小,每次循环完一轮表示一步。
  3. 需要对已经走过的位置进行记录,防止走回之前的节点。
  4. 由于pair没有内置的hash函数,所以无法被unordered_set包装,可以使用set来包装。
  5. 如果下一步即是终点,返回的步数需要加1。

C++版本:

/**
* Definition for a point.
* struct Point {
*     int x;
*     int y;
*     Point() : x(0), y(0) {}
*     Point(int a, int b) : x(a), y(b) {}
* };
*/
/**
* @param grid: a chessboard included 0 (false) and 1 (true)
* @param source: a point
* @param destination: a point
* @return: the shortest path 
*/
int shortestPath(vector<vector<bool>> &grid, Point source, Point destination) {
    // write your code here
    int result = 0;
    if (grid.empty() || grid.front().empty())
    {
        return result;
    }
    if (source.x == destination.x && source.y == destination.y)
    {
        return result;
    }

    queue<pair<int, int>> posQueue;
    set<pair<int, int>> posSet; // 用于记录已经走过的位置,由于pair没有内置hash函数,所以不能用unordered_set装pair
    posQueue.push({source.x, source.y});
    posSet.insert({source.x, source.y});
    int a[] = {1, 1, -1, -1, 2, 2, -2, -2};
    int b[] = {2, -2, 2, -2, 1, -1, 1, -1};
    while (!posQueue.empty())
    {
        int size = posQueue.size();
        for (int i = 0; i < size; i++)
        {
            int row = posQueue.front().first;
            int col = posQueue.front().second;
            posQueue.pop();
            for (int j = 0; j < 8; j++)
            {
                int newRow = row + a[j];
                int newCol = col + b[j];
                // 跳出棋盘无效
                if (!isValid(grid, newRow, newCol))
                {
                    continue;
                }
                // 跳到了终点
                if (newRow == destination.x && newCol == destination.y)
                {
                    return result + 1; // 下一步就是终点
                }
                // 如果该位置没有障碍,且没有到过
                if (grid.at(newRow).at(newCol) == 0
                && posSet.find(make_pair(newRow, newCol)) == posSet.end())
                {
                    posQueue.push({newRow, newCol});
                    posSet.insert(make_pair(newRow, newCol));
                }
            }
        }
        result++;
    }

    return -1;
}

bool isValid(vector<vector<bool>> & grid, int row, int col)
{
    int maxRow = grid.size() - 1;
    int maxCol = grid.front().size() - 1;
    if (row < 0 || row > maxRow)
    {
        return false;
    }
    if (col < 0 || col > maxCol)
    {
        return false;
    }

    return true;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值