【題目】1275. 找出井字棋的獲勝者
A 和 B 在一個 3 x 3 的網格上玩井字棋。
井字棋遊戲的規則如下:
- 玩家輪流將棋子放在空方格 (" ") 上。
- 第一個玩家 A 總是用 “X” 作爲棋子,而第二個玩家 B 總是用 “O” 作爲棋子。
- “X” 和 “O” 只能放在空方格中,而不能放在已經被佔用的方格上。
- 只要有 3 個相同的(非空)棋子排成一條直線(行、列、對角線)時,遊戲結束。
- 如果所有方塊都放滿棋子(不爲空),遊戲也會結束。
- 遊戲結束後,棋子無法再進行任何移動。
給你一個數組 moves,其中每個元素是大小爲 2 的另一個數組(元素分別對應網格的行和列),它按照 A 和 B 的行動順序(先 A 後 B)記錄了兩人各自的棋子位置。
如果遊戲存在獲勝者(A 或 B),就返回該遊戲的獲勝者;如果遊戲以平局結束,則返回 “Draw”;如果仍會有行動(遊戲未結束),則返回 “Pending”。
你可以假設 moves 都 有效(遵循井字棋規則),網格最初是空的,A 將先行動。
示例 1:
輸入:moves = [[0,0],[2,0],[1,1],[2,1],[2,2]]
輸出:"A"
解釋:"A" 獲勝,他總是先走。
"X " "X " "X " "X " "X "
" " -> " " -> " X " -> " X " -> " X "
" " "O " "O " "OO " "OOX"
示例 2:
輸入:moves = [[0,0],[1,1],[0,1],[0,2],[1,0],[2,0]]
輸出:"B"
解釋:"B" 獲勝。
"X " "X " "XX " "XXO" "XXO" "XXO"
" " -> " O " -> " O " -> " O " -> "XO " -> "XO "
" " " " " " " " " " "O "
示例 3:
輸入:moves = [[0,0],[1,1],[2,0],[1,0],[1,2],[2,1],[0,1],[0,2],[2,2]]
輸出:"Draw"
輸出:由於沒有辦法再行動,遊戲以平局結束。
"XXO"
"OOX"
"XOX"
示例 4:
輸入:moves = [[0,0],[1,1]]
輸出:"Pending"
解釋:遊戲還沒有結束。
"X "
" O "
" "
提示:
1 <= moves.length <= 9
moves[i].length == 2
0 <= moves[i][j] <= 2
moves 裏沒有重複的元素。
moves 遵循井字棋的規則。
【解題思路1】模擬
public String tictactoe(int[][] moves) {
int m = moves.length;
// 用數組記錄0-2行、0-2列、正對角線、副對角線是否已滿3個棋子
// count[0-2]對應0-2行、count[3-5]對應0-2列、count[6]對應正對角線、count[7]對應副對角線
int[] count = new int[8];
// 思路第2步已解釋爲何只需考慮最後一個落棋的人
// 倒序統計此人走棋情況
for(int i = m - 1; i >= 0; i -= 2) {
// 此棋對行的影響
count[moves[i][0]]++;
// 此棋對列的影響
count[moves[i][1] + 3]++;
// 此棋對正對角線的影響
if(moves[i][0] == moves[i][1])
count[6]++;
// 此棋對副對角線的影響 (
// 此處爲3x3的情況,其餘大小的棋盤可以類推
if(moves[i][0] + moves[i][1] == 2)
count[7]++;
// 滿3個棋子則勝利
if(count[moves[i][0]] == 3 || count[moves[i][1] + 3] == 3 ||
count[6] == 3 || count[7] == 3)
// A先B後 則總長度爲偶時 最後爲B 反之爲A
return m % 2 == 0 ? "B" : "A";
}
// 未勝時,棋盤未下滿則繼續
if(moves.length < 9)
return "Pending";
// 未勝時,棋盤下滿則平局結束
return "Draw";
}
【解題思路2】位運算
public String tictactoe(int[][] moves) {
// a, b record the moving results of A, B
int a = 0, b = 0, len = moves.length;
// ac records all cases of winning
int[] ac = {7, 56, 448, 73, 146, 292, 273, 84};
for(int i = 0; i < len; i ++){
// if i is add
if((i & 1) == 1){
// record the step result
b ^= 1 << (3 * moves[i][0] + moves[i][1]);
}
else {
a ^= 1 << (3 * moves[i][0] + moves[i][1]);
}
}
for(int i : ac){
// if the moving result contains the winning case in record, then win
if((a & i) == i){
return "A";
}
if((b & i) == i){
return "B";
}
}
// or judge the result by the amount of steps
return len == 9 ? "Draw" : "Pending";
}
【解題思路3】分開存分開判斷
class Solution {
public String tictactoe(int[][] moves) {
int [][] A = new int[3][3];
int [][] B = new int[3][3];
for (int i = 0; i < moves.length; i++) {
if (i%2 == 0){
A[moves[i][0]][moves[i][1]] = 1;
}else {
B[moves[i][0]][moves[i][1]] = 2;
}
}
for (int i = 0; i < 3; i++) {
if(A[i][0] + A[i][1] + A[i][2] == 3)
return "A";
}
for (int i = 0; i < 3; i++) {
if(A[0][i] + A[1][i] + A[2][i] == 3)
return "A";
}
if(A[0][0] + A[1][1] + A[2][2] == 3 || A[2][0] + A[1][1] + A[0][2] == 3)
return "A";
for (int i = 0; i < 3; i++) {
if(B[i][0] + B[i][1] + B[i][2] == 6)
return "B";
}
for (int i = 0; i < 3; i++) {
if(B[0][i] + B[1][i] + B[2][i] == 6)
return "B";
}
if(B[0][0] + B[1][1] + B[2][2] == 6 || B[2][0] + B[1][1] + B[0][2] == 6)
return "B";
if (moves.length == 9)
return "Draw";
return "Pending";
}
}