题目是给一个欧洲的棋盘,黑骑士的位置在(0,0)。给定了8个制定的方向,和目标位置。
求最少多少步可以到达目标位置。棋盘的范围是无限的。
刚刚开始觉得这道题目不难,典型的BFS,然后for 循环8个方向拿到新的位置。然后再继续BFS新的位置。
但是这样做下去会超时。
那么就得优化了。
1. 目标坐标是可以在4个坐标象限里的任意一个。 我们可以把目标左边转换成在第一象限。这样得到的结果一样。
2. 既然限制在第一象限,那从当前点沿着8个指定方向到达后的位置,是否要过滤下或者限制下。
有些到达的坐标位置是不合适的。
仔细分析这8个不同方向的路线,都是走两步拐一下。比如横着走两步,竖着拐一下。或者竖着走两步,再横着拐一下。
这么看起来,感觉横着最多往0点左侧走3步, 竖着也最多往0的下方走3步。
于是这样就能把8个方向走完后的坐便过滤掉一些不适用的。
3. 已经访问过的点,就不要再访问了。
之前一直在纠结,c++ unorder_set 无法把pair<int, int> 作为key。所以没有办法过滤重复的。
看了下其他大神的解法,发现很牛。
unorder_set 不可以,但是unorder_map 可以。
并且有的大神直接把坐标转换成字符串,然后再push到unorder_set里作为key。
以上的优化做完,这道题目就可以过关了。
class Solution {
public:
int minKnightMoves(int x, int y) {
//8 个制定的方向
vector<vector<int>> direc{
{2,1},
{1,2},
{-1,2},
{-2,1},
{-2,-1},
{-1,-2},
{1,-2},
{2,-1}
};
、、把问题限制在第一象限
int targetX = abs(x);
int targetY = abs(y);
int steps =0;
//用map来存储访问过的节点
map<pair<int, int>, bool> visited;
//bfs 老套路,queue
queue<pair<int, int>> myqueue;
//没找到一个点,就查一下是否是目标位置
if(0 == targetX && 0 == targetY)
return steps;
// 骑士目前坐标入队列
myqueue.push({0,0});
//更新方位过的坐标位置()0,0)
visited[{0,0}] = true;
while(!myqueue.empty())
{
int size = myqueue.size();
for(int i=0;i<size;i++)
{
pair<int, int> cur = myqueue.front();
myqueue.pop();
int x = cur.first;
int y = cur.second;
//遍历8个方向
for(int i=0;i<8;i++){
int newx = x+direc[i][0];
int newy = y+direc[i][1];
//检查新的坐标是否是目标位置
if(newx == targetX && newy == targetY)
return steps+1;
// 限制坐标位置为 x>=-3 y>=-3 并且新的坐标没有访问过。
else if(newx >=-3 && y >=-3 &&visited.find({newx, newy}) == visited.end() ) {
//入queue并标记该点访问过
myqueue.push({newx, newy});
visited[{newx, newy}] = true;
}
}
}
steps++;
}
return -1;
}
};