一、Problem
用字符串数组作为井字游戏的游戏板 board。当且仅当在井字游戏过程中,玩家有可能将字符放置成游戏板所显示的状态时,才返回 true。
该游戏板是一个 3 x 3 数组,由字符 " ",“X” 和 “O” 组成。字符 " " 代表一个空位。
以下是井字游戏的规则:
- 玩家轮流将字符放入空位(" ")中。
- 第一个玩家总是放字符 “X”,且第二个玩家总是放字符 “O”。
- “X” 和 “O” 只允许放置在空位中,不允许对已放有字符的位置进行填充。
- 当有 3 个相同(且非空)的字符填充任何行、列或对角线时,游戏结束。
- 当所有位置非空时,也算为游戏结束。
- 如果游戏结束,玩家不允许再放置字符。
输入: board = ["XOX", " X ", " "]
输出: false
解释: 玩家应该是轮流放置的
输入: board = ["XOX", "O O", "XOX"]
输出: true
说明:
游戏板 board 是长度为 3 的字符串数组,其中每个字符串 board[i] 的长度为 3
board[i][j] 是集合 {" ", “X”, “O”} 中的一个字符
二、Solution
方法一:分类讨论
思考
没啥规律,只能检查各种情况…
- O 的数量
⩽
\leqslant
⩽ X 的数量,且 X-O
⩽
\leqslant
⩽ 1;剩下的工作就是讨论仅有的两种情况:
- X ≥ O 的前提下,如果 X 和 O 一样多,但 X 赢了,不可能有这种情况啊,因为 X 都赢了,O 为什么还有;[“XXX”, " ", “OOO”]
- 如果 X - O = 1,但 O 赢了,不存在该情况;因为 O 是后手,在放 O 之前,O 一定严格小于 X 的个数
class Solution {
public:
bool chk(vector<string>& g, char c) {
for (int i=0; i<3; i++) {
if (g[i][0]==g[i][1] && g[i][1]==g[i][2] && g[i][2]==c) return true;
if (g[0][i]==g[1][i] && g[1][i]==g[2][i] && g[2][i]==c) return true;
}
if (g[0][0]==g[1][1] && g[1][1]==g[2][2] && g[2][2]==c) return true;
if (g[0][2]==g[1][1] && g[1][1]==g[2][2] && g[2][2]==c) return true;
return false;
}
bool validTicTacToe(vector<string>& g) {
int X=0, O=0, n=3;
for (int i=0; i<n; i++)
for (int j=0; j<n; j++) {
if (g[i][j] == 'O') O++;
else if (g[i][j] == 'X') X++;
}
// x 先手还比后手的o少或者x多放了2~n个,还合法吗
if (O > X || X-O > 1) return false;
// X-O == 0 or X-O == 1
// X>=O的前提下,如果X和O一样多,但X赢了,不可能有这种情况啊,X都赢了,O为什么还有,["XXX", " ", "OOO"]
if (O == X && chk(g, 'X')) return false;
// 如果X比O多一个,但O赢了,也不可能
if (X-O==1 && chk(g, 'O')) return false;
return true;
}
};
复杂度分析
- 时间复杂度: O ( 1 ) O(1) O(1),
- 空间复杂度: O ( 1 ) O(1) O(1)