题目地址:
https://leetcode.com/problems/design-snake-game/
设计二维矩阵内的贪吃蛇游戏。题目保证蛇在吃了一个食物后,下一个食物不会处于蛇的身体上。要动态返回游戏的得分,每当蛇吃了一个食物后,身体会变长,并且得分会加 1 1 1;如果蛇走到了边界之外,或者走到了自己身上,得分返回 − 1 -1 −1。
思路是用双端队列,队头表示蛇头,队尾表示蛇尾,这样每走一步的时候,就可以将新走到的那个格子入队,并且队头出队。之所以要用双端队列,是因为需要用front()
方法看一下蛇头在什么位置,才能接着走下一步。同时为了节省空间和查询迅速(这里查询迅速主要是为了查一下是否蛇头走到了自身),可以用一个哈希表记录蛇的身体的各个点的坐标。代码如下:
class SnakeGame {
#define x first
#define y second
using PII = pair<int, int>;
private:
int m, n;
int score, food_idx;
vector<vector<int>> food;
deque<PII> dq;
struct phash {
int operator()(const PII &p) const {
return hash<int>()(p.x) ^ hash<int>()(p.y);
}
};
unordered_set<PII, phash> st;
public:
SnakeGame(int width, int height, vector<vector<int>> &food)
: n(width), m(height), score(0), food_idx(0), food(std::move(food)) {
dq.emplace_back(0, 0);
st.insert({0, 0});
}
int move(string dir) {
char d = dir[0];
int x = dq.front().x, y = dq.front().y;
x += d == 'D' ? 1 : 0;
x += d == 'U' ? -1 : 0;
y += d == 'L' ? -1 : 0;
y += d == 'R' ? 1 : 0;
if (x < 0 || x >= m || y < 0 || y >= n) return -1;
pair cur{x, y};
if (food_idx < food.size())
if (x == food[food_idx][0] && y == food[food_idx][1]) {
score++;
dq.emplace_front(x, y);
st.insert({x, y});
food_idx++;
return score;
}
auto tail = dq.back();
dq.pop_back();
st.erase(tail);
// 撞到自己了
if (st.count(cur)) return -1;
dq.emplace_front(x, y);
st.insert({x, y});
return score;
}
};
时空复杂度 O ( d ) O(d) O(d), d d d指direction。
注解:
1、move()
方法的最后,如果没走到食物,一定要先将蛇尾出队,再判断是否蛇头咬到自己,否则会出错。因为当蛇走了一步的时候,蛇尾会跟着一起走,即使蛇头下一步会走到蛇尾原先的地方,但由于蛇尾已经走掉了,此时并不会咬到自己。
2、本题使用双端队列是不得已而为之,如果不把整条蛇的格点全记录下来的话,即使知道蛇头会走到哪里,也很难判断蛇尾跟到了哪里,最困难的地方在于不知道蛇尾的下一步的方向。用双端队列就很好解决了这个问题。