Skip to content

Commit f87593e

Browse files
authored
feat: add solutions to lc problem: No.0353 (doocs#1322)
No.0353.Design Snake Game
1 parent 912702c commit f87593e

File tree

7 files changed

+895
-1
lines changed

7 files changed

+895
-1
lines changed

solution/0300-0399/0353.Design Snake Game/README.md

Lines changed: 314 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,22 +65,335 @@ snakeGame.move("U"); // 返回 -1 ,蛇与边界相撞,游戏结束
6565

6666
<!-- 这里可写通用的实现逻辑 -->
6767

68+
**方法一:双端队列模拟**
69+
70+
我们可以使用双端队列来模拟蛇的移动。
71+
72+
定义一个双端队列 $q$,其中保存蛇的身体坐标,队头为蛇头,队尾为蛇尾。同时使用一个集合 $vis$ 来保存蛇的身体坐标,用于快速判断蛇头是否与蛇身相撞。
73+
74+
定义一个变量 $score$ 来保存蛇的得分,初始值为 $0$;定义一个变量 $idx$ 来保存当前食物的索引,初始值为 $0$。
75+
76+
每次移动时,首先判断蛇头是否与边界相撞,如果相撞则游戏结束,返回 $-1$;否则,判断蛇头是否与食物重合,如果重合则蛇的得分加 $1$,同时食物索引 $idx$ 加 $1$;否则,蛇的身体长度不变,需要将蛇尾从队尾弹出,并从集合 $vis$ 中删除对应的坐标。
77+
78+
然后,判断蛇头是否与蛇身相撞,如果相撞则游戏结束,返回 $-1$;否则,将蛇头的坐标加入集合 $vis$ 中,并从队头加入蛇头的坐标。
79+
80+
最后,返回蛇的得分 $score$。
81+
82+
时间复杂度 $O(k)$,空间复杂度 $O(k)$,其中 $k$ 为移动的次数。
83+
6884
<!-- tabs:start -->
6985

7086
### **Python3**
7187

7288
<!-- 这里可写当前语言的特殊实现逻辑 -->
7389

7490
```python
75-
91+
class SnakeGame:
92+
def __init__(self, width: int, height: int, food: List[List[int]]):
93+
self.m = height
94+
self.n = width
95+
self.food = food
96+
self.score = 0
97+
self.idx = 0
98+
self.q = deque([(0, 0)])
99+
self.vis = {(0, 0)}
100+
101+
def move(self, direction: str) -> int:
102+
i, j = self.q[0]
103+
x, y = i, j
104+
match direction:
105+
case "U":
106+
x -= 1
107+
case "D":
108+
x += 1
109+
case "L":
110+
y -= 1
111+
case "R":
112+
y += 1
113+
if x < 0 or x >= self.m or y < 0 or y >= self.n:
114+
return -1
115+
if (
116+
self.idx < len(self.food)
117+
and x == self.food[self.idx][0]
118+
and y == self.food[self.idx][1]
119+
):
120+
self.score += 1
121+
self.idx += 1
122+
else:
123+
self.vis.remove(self.q.pop())
124+
if (x, y) in self.vis:
125+
return -1
126+
self.q.appendleft((x, y))
127+
self.vis.add((x, y))
128+
return self.score
129+
130+
131+
# Your SnakeGame object will be instantiated and called as such:
132+
# obj = SnakeGame(width, height, food)
133+
# param_1 = obj.move(direction)
76134
```
77135

78136
### **Java**
79137

80138
<!-- 这里可写当前语言的特殊实现逻辑 -->
81139

82140
```java
141+
class SnakeGame {
142+
private int m;
143+
private int n;
144+
private int[][] food;
145+
private int score;
146+
private int idx;
147+
private Deque<Integer> q = new ArrayDeque<>();
148+
private Set<Integer> vis = new HashSet<>();
149+
150+
public SnakeGame(int width, int height, int[][] food) {
151+
m = height;
152+
n = width;
153+
this.food = food;
154+
q.offer(0);
155+
vis.add(0);
156+
}
157+
158+
public int move(String direction) {
159+
int p = q.peekFirst();
160+
int i = p / n, j = p % n;
161+
int x = i, y = j;
162+
if ("U".equals(direction)) {
163+
--x;
164+
} else if ("D".equals(direction)) {
165+
++x;
166+
} else if ("L".equals(direction)) {
167+
--y;
168+
} else {
169+
++y;
170+
}
171+
if (x < 0 || x >= m || y < 0 || y >= n) {
172+
return -1;
173+
}
174+
if (idx < food.length && x == food[idx][0] && y == food[idx][1]) {
175+
++score;
176+
++idx;
177+
} else {
178+
int t = q.pollLast();
179+
vis.remove(t);
180+
}
181+
int cur = f(x, y);
182+
if (vis.contains(cur)) {
183+
return -1;
184+
}
185+
q.offerFirst(cur);
186+
vis.add(cur);
187+
return score;
188+
}
189+
190+
private int f(int i, int j) {
191+
return i * n + j;
192+
}
193+
}
194+
195+
/**
196+
* Your SnakeGame object will be instantiated and called as such:
197+
* SnakeGame obj = new SnakeGame(width, height, food);
198+
* int param_1 = obj.move(direction);
199+
*/
200+
```
201+
202+
### **C++**
203+
204+
```cpp
205+
class SnakeGame {
206+
public:
207+
SnakeGame(int width, int height, vector<vector<int>>& food) {
208+
m = height;
209+
n = width;
210+
this->food = food;
211+
score = 0;
212+
idx = 0;
213+
q.push_back(0);
214+
vis.insert(0);
215+
}
216+
217+
int move(string direction) {
218+
int p = q.front();
219+
int i = p / n, j = p % n;
220+
int x = i, y = j;
221+
if (direction == "U") {
222+
--x;
223+
} else if (direction == "D") {
224+
++x;
225+
} else if (direction == "L") {
226+
--y;
227+
} else {
228+
++y;
229+
}
230+
if (x < 0 || x >= m || y < 0 || y >= n) {
231+
return -1;
232+
}
233+
if (idx < food.size() && x == food[idx][0] && y == food[idx][1]) {
234+
++score;
235+
++idx;
236+
} else {
237+
int tail = q.back();
238+
q.pop_back();
239+
vis.erase(tail);
240+
}
241+
int cur = f(x, y);
242+
if (vis.count(cur)) {
243+
return -1;
244+
}
245+
q.push_front(cur);
246+
vis.insert(cur);
247+
return score;
248+
}
249+
250+
private:
251+
int m;
252+
int n;
253+
vector<vector<int>> food;
254+
int score;
255+
int idx;
256+
deque<int> q;
257+
unordered_set<int> vis;
258+
259+
int f(int i, int j) {
260+
return i * n + j;
261+
}
262+
};
263+
264+
/**
265+
* Your SnakeGame object will be instantiated and called as such:
266+
* SnakeGame* obj = new SnakeGame(width, height, food);
267+
* int param_1 = obj->move(direction);
268+
*/
269+
```
270+
271+
### **Go**
272+
273+
```go
274+
type SnakeGame struct {
275+
m int
276+
n int
277+
food [][]int
278+
score int
279+
idx int
280+
q []int
281+
vis map[int]bool
282+
}
283+
284+
func Constructor(width int, height int, food [][]int) SnakeGame {
285+
return SnakeGame{height, width, food, 0, 0, []int{0}, map[int]bool{}}
286+
}
287+
288+
func (this *SnakeGame) Move(direction string) int {
289+
f := func(i, j int) int {
290+
return i*this.n + j
291+
}
292+
p := this.q[0]
293+
i, j := p/this.n, p%this.n
294+
x, y := i, j
295+
if direction == "U" {
296+
x--
297+
} else if direction == "D" {
298+
x++
299+
} else if direction == "L" {
300+
y--
301+
} else {
302+
y++
303+
}
304+
if x < 0 || x >= this.m || y < 0 || y >= this.n {
305+
return -1
306+
}
307+
if this.idx < len(this.food) && x == this.food[this.idx][0] && y == this.food[this.idx][1] {
308+
this.score++
309+
this.idx++
310+
} else {
311+
t := this.q[len(this.q)-1]
312+
this.q = this.q[:len(this.q)-1]
313+
this.vis[t] = false
314+
}
315+
cur := f(x, y)
316+
if this.vis[cur] {
317+
return -1
318+
}
319+
this.q = append([]int{cur}, this.q...)
320+
this.vis[cur] = true
321+
return this.score
322+
}
323+
324+
/**
325+
* Your SnakeGame object will be instantiated and called as such:
326+
* obj := Constructor(width, height, food);
327+
* param_1 := obj.Move(direction);
328+
*/
329+
```
83330

331+
### **TypeScript**
332+
333+
```ts
334+
class SnakeGame {
335+
private m: number;
336+
private n: number;
337+
private food: number[][];
338+
private score: number;
339+
private idx: number;
340+
private q: number[];
341+
private vis: Set<number>;
342+
343+
constructor(width: number, height: number, food: number[][]) {
344+
this.m = height;
345+
this.n = width;
346+
this.food = food;
347+
this.score = 0;
348+
this.idx = 0;
349+
this.q = [0];
350+
this.vis = new Set([0]);
351+
}
352+
353+
move(direction: string): number {
354+
const p = this.q[0];
355+
const i = Math.floor(p / this.n);
356+
const j = p % this.n;
357+
let x = i;
358+
let y = j;
359+
if (direction === 'U') {
360+
--x;
361+
} else if (direction === 'D') {
362+
++x;
363+
} else if (direction === 'L') {
364+
--y;
365+
} else {
366+
++y;
367+
}
368+
if (x < 0 || x >= this.m || y < 0 || y >= this.n) {
369+
return -1;
370+
}
371+
if (
372+
this.idx < this.food.length &&
373+
x === this.food[this.idx][0] &&
374+
y === this.food[this.idx][1]
375+
) {
376+
++this.score;
377+
++this.idx;
378+
} else {
379+
const t = this.q.pop()!;
380+
this.vis.delete(t);
381+
}
382+
const cur = x * this.n + y;
383+
if (this.vis.has(cur)) {
384+
return -1;
385+
}
386+
this.q.unshift(cur);
387+
this.vis.add(cur);
388+
return this.score;
389+
}
390+
}
391+
392+
/**
393+
* Your SnakeGame object will be instantiated and called as such:
394+
* var obj = new SnakeGame(width, height, food)
395+
* var param_1 = obj.move(direction)
396+
*/
84397
```
85398

86399
### **...**

0 commit comments

Comments
 (0)