leetcode 794. Valid Tic-Tac-Toe State

13 篇文章 0 订阅
4 篇文章 0 订阅

leetcode 794. Valid Tic-Tac-Toe State

题目描述

A Tic-Tac-Toe board is given as a string array board. Return True if and only if it is possible to reach this board position during the course of a valid tic-tac-toe game.

The board is a 3 x 3 array, and consists of characters " ", "X", and "O". The " " character represents an empty square.

Here are the rules of Tic-Tac-Toe:

  • Players take turns placing characters into empty squares (” “).
  • The first player always places “X” characters, while the second player always places “O” characters.
  • “X” and “O” characters are always placed into empty squares, never filled ones.
  • The game ends when there are 3 of the same (non-empty) character filling any row, column, or diagonal.
  • The game also ends if all squares are non-empty.
  • No more moves can be played if the game is over.

Note:

  • board is a length-3 array of strings, where each string board[i] has length 3.
  • Each board[i][j] is a character in the set {" ", "X", "O"}.

Difficulty: Medium
794. Valid Tic-Tac-Toe State


中文描述
在一个井字格游戏中,先手持有X的棋子,后手持有O的棋子,两者交替在井字格的空位中交替下子,当任意一方使得斜着,竖着或是横着的三个位置为同一种棋子时,该人获胜,游戏结束。或者双方都没有空格下子时,游戏结束。现在给你一个井字格游戏的当前情况,问你在满足上述条件下,该情况是否成立。


输入格式
输入一个列表boardboard的每个字符串表示游戏中的一行的情况。


Examples:

  1. Input: board = [“O “, ” “, ” “]
    Output: false
    解释:
    先手是X,所以不可能出现开场为O的情况。

  2. Input: board = [“XOX”, ” X “, ” “]
    Output: false
    解释:
    X和O是交替进行,不可能出现下了3次X却只下了1次O。

  3. Input: board = [“XXX”, ” “, “OOO”]
    Output: false
    解释:
    因为X先手,在出现”XXX”的时候,比赛就已经结束了,所以不可能再出现”OOO”的情况。

  4. Input: board = [“XOX”, “O O”, “XOX”]
    Output: true
    解释:
    显然这种情况是可以做到的。


解答思路

  • 解法一:dfs

    1.我们根据目标状态,一步一步下棋。考虑用dfs遍历的方式遍历所有不违反规则的步骤,看是否存在一种下法能够到达目标状态。

    2.我们先把目标状态把所有X,O的位置记录下来,之后每次下棋都从对应棋子的位置中选择一步。

    3.每下完一步判断是否达到目标状态,是的话就返回true,否则判断该局游戏是否已经结束,如果是的话则返回上一步,重新选择下棋的位置。直到无棋可下的情况,说明该目标状态无解,返回false。

    4.详细的时间复杂度不好估计,与X和O的棋子个数相关。估计了下最差情况应该是 n! n ! ,这边 n n <script type="math/tex" id="MathJax-Element-4593">n</script>不会大于9,所以算是常数时间。

  • 解法二:数学分析

    1.其实可以不用像上述方法遍历所有情况,可以通过数学分析来判断该局游戏是否成立。

    2.首先由于X先手,则X的棋子个数肯定不会小于O棋子的个数,如果违反这点该局游戏不会成立,如案例2.

    3.并且根据场上每个棋子的个数,我们也能判断最后一手是下X还是O。如案例三情况。在比如[“X X”,”OXX”,”OOO”]这样的情况。如果下三个O先完成,则游戏应该已经结束。但是,由于O棋摆放在3个的时候没有必然获胜,则X棋存在某种情况可以继续下下去,即存在[“X X”,”O X”,” OO”]–>[“X X”,”OXX”,” OO”]–>[“X X”,”OXX”,”OOO”]。所以只要前一个玩家没有必然获胜,下一个玩家就还能继续。如果在下X前O已经必然获胜,则该局游戏不成立(对O也是同样道理)。

    4.时间复杂度估计,只要统计X和O的个数和判断是否提前结束,所以是常数时间内。


代码

解法一,dfs,

class Solution(object):
    def validTicTacToe(self, board):
        """
        :type board: List[str]
        :rtype: bool
        145ms
        """
        def is_win(mark, board):
            left2right = ''
            right2left = ''
            target = mark * 3
            for i in range(3):
                # 一行都是
                if ''.join(board[i]) == target:
                    return True
                # 一列都是
                s = board[0][i] + board[1][i] + board[2][i]
                if s == target:
                    return True
                # 从左到右对角线
                left2right += board[i][i]
                # 从右到左对角线
                right2left += board[-1 - i][i]
            if left2right == target or right2left == target:
                return True
            return False

        dicts = {'X':[], 'O':[], ' ':[]}
        for i in range(3):
            for j in range(3):
                dicts[board[i][j]].append((i, j))
        new_board = [[' ' for _ in range(3)] for _ in range(3)]

        def dfs(count, dicts, new_board):
            flag = True
            # 判断是否已经到达目标状态
            for i in range(3):
                if ''.join(new_board[i]) != board[i]:
                    flag = False
                    break
            if flag:
                return True
            # 当前下的棋子
            if count % 2 == 0:
                mark = 'X'
            else:
                mark = 'O'

            before_mark = 'X' if mark == 'O' else 'O'
            # 判断上一个玩家是不是已经赢得比赛
            if is_win(before_mark, new_board):
                return False

            for i in dicts[mark]:
                new_board[i[0]][i[1]] = mark
                copy_dicts = {}
                copy_dicts['X'] = dicts['X'][:]
                copy_dicts['O'] = dicts['O'][:]
                copy_dicts[mark].remove(i)
                if dfs(count + 1, copy_dicts, new_board):
                    return True
                new_board[i[0]][i[1]] = ''

            return False

        return dfs(0, dicts, new_board)

解法二,数学分析:

class Solution(object):
    def validTicTacToe(self, board):
        """
        :type board: List[str]
        :rtype: bool
        34ms
        """
        # 判断是否已经获胜
        def is_win(mark, board):
            left2right = ''
            right2left = ''
            target = mark * 3
            for i in range(3):
                # 一行都是
                if board[i] == target:
                    return True
                # 一列都是
                s = board[0][i] + board[1][i] + board[2][i]
                if s == target:
                    return True
                # 从左到右对角线
                left2right += board[i][i]
                # 从右到左对角线
                right2left += board[-1 - i][i]
            if left2right == target or right2left == target:
                return True
            return False

        count_X = 0
        count_O = 0
        # 统计X,O各自的个数
        for s in board:
            for char in s:
                if char == 'X':
                    count_X += 1
                elif char == 'O':
                    count_O += 1
        # 判断OX个数是否一致,一致的话需要判断X之前是不是已经赢了
        if count_O == count_X:
            mark = 'X'
        # 判断OX个数是否一致,X比O大一的话需要判断O之前是不是已经赢了
        elif count_X == count_O + 1:
            mark = 'O'
        # 否则两者差了2个以上,肯定错误
        else:
            return False
        # 这种情况一定能够成立
        if count_O < 3:
            return True
        return not is_win(mark, board)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值