leetcode
leetcode 3001 ~ 3050
黑白翻转棋

黑白翻转棋

难度:

标签:

题目描述

English description is not available for the problem. Please switch to Chinese.

代码结果

运行时间: 42 ms, 内存: 16.0 MB


/*
 * 思路:
 * 使用 Java Stream 来简化代码流程,依然是遍历所有空位并计算最多翻转的棋子数。
 * 使用 List 存储所有可能的翻转数量,最后取最大值。
 */
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class FlipChessStream {
    private static final int[][] DIRECTIONS = {
        {0, 1}, {0, -1}, {1, 0}, {-1, 0}, {1, 1}, {-1, -1}, {1, -1}, {-1, 1}
    };

    public int maxFlip(String[] chessboard) {
        return Arrays.stream(chessboard)
            .flatMapToInt(row -> Arrays.stream(row.split("")))
            .collect(Collectors.toList())
            .stream()
            .mapToInt(ch -> chessboard.length * chessboard[0].length())
            .map(idx -> {
                int x = idx / chessboard[0].length();
                int y = idx % chessboard[0].length();
                return chessboard[x].charAt(y) == '.' ? countFlips(chessboard, x, y) : 0;
            })
            .max().orElse(0);
    }

    private int countFlips(String[] board, int x, int y) {
        return Arrays.stream(DIRECTIONS)
            .mapToInt(dir -> flipDirection(board, x, y, dir[0], dir[1]))
            .sum();
    }

    private int flipDirection(String[] board, int x, int y, int dx, int dy) {
        int nx = x + dx, ny = y + dy;
        int flipCount = 0;
        boolean canFlip = false;
        while (nx >= 0 && nx < board.length && ny >= 0 && ny < board[0].length()) {
            if (board[nx].charAt(ny) == '.') break;
            if (board[nx].charAt(ny) == 'X') {
                canFlip = true;
                break;
            }
            flipCount++;
            nx += dx;
            ny += dy;
        }
        return canFlip ? flipCount : 0;
    }
}

解释

方法:

这个题解的思路是通过模拟下棋的过程来寻找最优的下棋位置。首先,遍历棋盘上的每一个空位('.'),尝试在这个位置放置黑棋('X'),然后从这个位置向八个方向(上、下、左、右、四个对角线方向)探索,查看是否可以通过放置这个棋子来翻转周围的白棋('O')。如果在某个方向上,从当前位置出发,连续遇到白棋后再遇到一个黑棋,那么这一路上的所有白棋都可以被翻转成黑棋。每次模拟下棋后,计算可以翻转的白棋数量,并更新最大值。最后返回最大的翻转数量。

时间复杂度:

O(n^4)

空间复杂度:

O(n^2)

代码细节讲解

🦆
在这个算法中,如果棋盘的大小变得更大,这种模拟方法的效率会如何变化?
这种模拟方法的效率会随着棋盘大小的增加而显著降低。算法的时间复杂度主要由两部分组成:首先是遍历每一个空位尝试放置棋子,其次是从每个空位向八个方向进行搜索。如果棋盘的维度是m×n,那么空位的数量最多是m×n个,而每个空位都可能需要进行八个方向的深度搜索,每个方向的搜索长度最多为棋盘的最大维度(max(m,n))。因此,总的时间复杂度大约是O(m*n*8*max(m,n)),即O(m^2*n^2)至O(m*n^3),这是一个多项式级增长的时间复杂度。对于大型棋盘,这种算法可能变得非常缓慢,特别是在空间复杂度也随之增大的情况下。
🦆
在模拟过程中,为什么选择在一个方向上如果遇到白棋后又遇到黑棋就进行翻转,这是否确保了翻转的合法性?
这种选择确保了翻转的合法性,因为根据黑白翻转棋(通常称为奥赛罗或Reversi)的规则,一个有效的落子必须夹住对手的一个或多个棋子(在这里是白棋),并且落子的两端必须是相同颜色的棋子。在算法中,从放置的黑棋开始向某个方向搜索,如果连续遇到白棋并最终遇到黑棋,这意味着这一系列的白棋被两个黑棋夹住,因此可以合法地翻转这些白棋。如果搜索到的是边界或者空位,则不满足翻转条件。这种模拟确实反映了游戏的实际规则。
🦆
算法中使用了一个队列来存储可能被翻转的棋子位置,这个队列的作用是什么,是否有其他数据结构可以替代来提高效率?
队列在这里主要用于存储和追踪在翻转过程中可能需要进一步考虑的棋子位置。这种方法类似于宽度优先搜索(BFS),队列用来确保每次处理的是先发现的棋子位置。使用队列可以帮助算法系统地检查所有需要翻转的棋子,并逐一更新棋盘状态。尽管队列是有效的,但在某些情况下可以考虑使用栈来代替队列,这将改变搜索的顺序为深度优先搜索(DFS)。DFS可能在某些方向的搜索中更快地达到边界条件,从而减少部分计算。然而,这种替换可能对算法的总体性能影响不大,因为主要的时间消耗在于遍历和检查棋盘的每个位置。

相关问题