@@ -65,22 +65,335 @@ snakeGame.move("U"); // 返回 -1 ,蛇与边界相撞,游戏结束
65
65
66
66
<!-- 这里可写通用的实现逻辑 -->
67
67
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
+
68
84
<!-- tabs:start -->
69
85
70
86
### ** Python3**
71
87
72
88
<!-- 这里可写当前语言的特殊实现逻辑 -->
73
89
74
90
``` 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)
76
134
```
77
135
78
136
### ** Java**
79
137
80
138
<!-- 这里可写当前语言的特殊实现逻辑 -->
81
139
82
140
``` 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
+ ```
83
330
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
+ */
84
397
```
85
398
86
399
### ** ...**
0 commit comments