SWE · Coding

N×N多玩家井字棋:三连胜判定与非法操作

玩家轮流落不同单字符,任意方向连续三枚即胜。

题目重点:玩家轮流落不同单字符,任意方向连续三枚即胜。

题意与假设

这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。

  • N>0,players为非空唯一单字符列表,轮流按列表顺序;棋盘满且无人三连为tie。
  • 目标三连而不是整行N枚;game结束后拒绝继续落子。N<3不会产生三连,只可能tie。

解题路线

  1. 保存稀疏board映射。play先检查状态、轮次、范围与占位,然后落当前player。
  2. 新出现胜利必须经过新子,所以仅沿竖/横/两对角轴分别向两侧最多查两步,累计同player连续长度≥3即胜。
  3. 若没有胜且满盘则tie,否则切下个turn。getStatus读状态。若目标改为整行,可每player维护行/列/两对角计数,但不能用全行累计当三连:中间空隙仍无胜。

手动走一遍

5×5中A在(1,1),(1,2),(1,3)连续三枚便胜,无需一整行5枚。同玩家(1,0),(1,2),(1,4)三枚但不连续,无胜;occupied位置重放应拒绝,不多计步。

Python 参考实现

下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。

代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。

class TicTacToe:
    def __init__(self,n,players):
        if n<=0 or not players or any(type(p) is not str or len(p)!=1 for p in players) or len(players)!=len(set(players)):raise ValueError("positive n and unique players required")
        self.n=n;self.players=list(players);self.turn=0;self.board={};self.status='PLAYING'

    def getStatus(self):return self.status

    def play(self,player,r,c):
        if self.status!='PLAYING' or player!=self.players[self.turn] or not 0<=r<self.n or not 0<=c<self.n or (r,c) in self.board:raise ValueError("illegal move")
        self.board[r,c]=player
        for dr,dc in ((1,0),(0,1),(1,1),(1,-1)):
            length=1
            for sign in (-1,1):
                for step in range(1,3):
                    if self.board.get((r+sign*step*dr,c+sign*step*dc))==player:length+=1
                    else:break
            if length>=3:self.status=('WIN',player);return self.status
        if len(self.board)==self.n*self.n:self.status='TIE'
        else:self.turn=(self.turn+1)%len(self.players)
        return self.status

复杂度

三连常数方向/步数,play期望O(1),getStatus O(1),棋盘O(已下步数)最多O(N²)。若连L目标,直接扫描O(L)。

易错点与追问

容易失分的边界

  • 忽略轮次会让同一玩家连续落子。
  • board.get不存在为None,玩家身份必须不与缺失哨兵混淆。

继续深挖

  • 全行胜且K玩家如何保持每玩家计数与空间上界?
  • 撤销上一步时如何恢复turn、status及计数?
✓已复制微信号!