【Leetcode】794. Valid Tic-Tac-Toe State

题目地址:

https://leetcode.com/problems/valid-tic-tac-toe-state/

给定一个方阵,两个人玩游戏,每个人可以在某个方格填上带有自己标记的记号,玩家 1 1 1标记的是 X X X,玩家 2 2 2标记的是 O O O。如果有某一行或者某一列或者某个对角线(指的是最长的两条对角线)全标记了相同的记号,则判定那个玩家获胜。开局玩家 1 1 1先走,然后两个玩家轮流走。如果某个玩家获胜则游戏立即停止。

现在给定一个盘面,问该盘面是否合法。合法的意思是存在两个玩家的走法,使得某个时刻盘面刚好等于上面给定的盘面。

思路是先考虑必要条件,然后尝试证明它们也是充分条件。容易知道的必要条件有:
1、合法盘面必然 X X X的个数 x x x O O O的个数 o o o满足 x = o x=o x=o或者 x = o + 1 x=o+1 x=o+1,因为 X X X先走,所以 X X X要么和 O O O一样多,要么只多一个;
2、如果 x = o x=o x=o,那么 X X X不能赢,因为最后一步是 O O O走,如果 X X X赢了那应该游戏停止,不应该还能继续;
3、如果 x = o + 1 x=o+1 x=o+1,那么 O O O不能赢,理由同上。

下面证明上面的三个条件合起来也是充分条件,即如果某个条件同时满足上面三个条件,那么这个盘面必然合法。

1、 x = o x=o x=o:如果 O O O没赢,那么随便走;如果 O O O赢了,那么将其赢了的那个行或者列或者对角线其中一个 O O O设定为 O O O的最后一步,别的步数随便走;
2、 x = o + 1 x=o+1 x=o+1:如果 X X X没赢,那么随便走;如果 X X X赢了,那么将其赢了的那个行或者列或者对角线其中一个 X X X设定为 X X X的最后一步,别的步数随便走;

综上所述上面三个条件是充要条件。

具体实现上,可以将两个玩家分别映射到 − 1 -1 1 1 1 1上,然后用一个数组标记每一行、列、对角线的数字和,每次一个玩家走的时候,就将格子对应的行、列、对角线上面的数字加上对应的 − 1 -1 1或者 1 1 1。这样只要发现某个数字和为 n n n或者 − n -n n就知道某个玩家赢了。代码如下:

public class Solution {
    public boolean validTicTacToe(String[] board) {
        if (board == null || board.length == 0 || board[0].length() == 0) {
            return true;
        }
        
        int n = board.length;
        // 记录每一行、列、对角线对应的数字和
        int[] rows = new int[n], cols = new int[n];
        int diag = 0, antidiag = 0;
        // 记录盘面里一共有多少个O和X
        int ocount = 0, xcount = 0;
        // 记录盘面里O是否赢了,X是否赢了
        boolean owin = false, xwin = false;
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[i].length(); j++) {
                char ch = board[i].charAt(j);
                rows[i] += mapping(ch);
                cols[j] += mapping(ch);
                if (i == j) {
                    diag += mapping(ch);
                }
                if (i + j == n - 1) {
                    antidiag += mapping(ch);
                }
                
                if (ch == 'X') {
                    xcount++;
                } else if (ch == 'O') {
                    ocount++;
                }
                
                if (rows[i] == n || cols[j] == n || diag == n || antidiag == n) {
                    owin = true;
                }
    
                if (rows[i] == -n || cols[j] == -n || diag == -n || antidiag == -n) {
                    xwin = true;
                }
            }
        }
        
        // 分别判断一下三个条件
        if (xcount < ocount || xcount > ocount + 1) {
            return false;
        }
        
        if (xcount == ocount && xwin) {
            return false;
        }
        
        if (xcount == ocount && owin) {
            return false;
        }
        
        return true;
    }
    // 将X映射为-1,O映射为O,别的映射为0
    private int mapping(char c) {
        if (c == 'X') {
            return -1;
        } else if (c == 'O') {
            return 1;
        } else {
            return 0;
        }
    }
}

时间复杂度 O ( n 2 ) O(n^2) O(n2),空间 O ( n ) O(n) O(n) n n n为盘面边长。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值