题目
一个N * N的棋盘,起点是左下角,一次可以走 1-6步
从左下角开始棋盘编号从下往上蛇形递增
7 8 9
6 5 4
1 2 3
如上图(其实不是图)所示:
起点是 1,终点是 9(N*N) (1-9为棋盘编号)
每格棋盘单元格内容一般是-1
如果棋盘单元格内容不是-1,而是一个数字,
则如果跳到这里,就要传送到指定数字编号的棋盘单元格中
-1 -1 -1
-1 -1 -1
-1 -1 8
如图:走到第三个格的位置,就会自动被传送到8的位置
问:到终点的最少次数?
注意:如果传送到的棋盘单元格内容也不是-1,无法进行二次传送
题目解析
- 看起来实际上就两步:
- 把二维数组转一维,从下往上之字形遍历即可.
- 广度优先搜索找到最先到终点的位置.
然而我…:
- 个人总结的坑点:
- 转一维的坑点:是从最后一行开始转的,并且长度为奇偶都得分开讨论。
- 写扩散的坑点:如果你像我一样是通过判断扩散出的元素来找终点的话,那么这个点着实是坑,由于扩散入队的元素形式有两种,你又得分开讨论了。如果是直接从每次出队扩散的元素进行判断,那么这个坑点就是没有的。。。
解题代码
class Solution {
public:
//由于有传送门,可能将其传送回头,所以还是需要visit数组防止回头路
int snakesAndLadders(vector<vector<int>>& board) {
//把终点有传送门的直接pass
if(board[0][0]!=-1)return -1;
//先转一维数组(这样就不需要考虑遍历顺序这回事了)
n = board.size();
change(board);
//以编号进行bfs(1~n*n)
Q.push(1);
visit.insert(1);
//bfs模板
int step = 0;
while(!Q.empty()){
for(int i=Q.size();i>0;i--){
int t = Q.front();Q.pop();
//根据t进行下一步扩散
if(check(t))
return step+1;
}
step++;
}
return -1;
}
private:
queue<int>Q;
vector<int>t_board;
unordered_set<int>visit;
int n;
void change(vector<vector<int>>&board){
t_board.resize(n*n+1);
int index=0;
for (int i = n-1; i >= 0; i--) {
//取最下方为第一行,奇数行正向,偶数行反向
int row=n - i;
if(row % 2 ==1){
for (int j = 0; j < n; j++) {
t_board[++index]=board[i][j];
}
}else{
for (int j = n-1; j >= 0; j--) {
t_board[++index]=board[i][j];
}
}
}
}
bool check(int t){
for(int i=1;i<=6;i++){
if(t+i>n*n)
continue;
if(t_board[t+i]!=-1&&!visit.count(t_board[t+i])){
if(t_board[t+i]==n*n)return true;
visit.insert(t_board[t+i]);
Q.push(t_board[t+i]);
}else if(t_board[t+i]==-1&&!visit.count(t+i)){if(t+i==n*n)return true;
visit.insert(t+i);
Q.push(t+i);
}
}
return false;
}
};