leetcode
leetcode 3001 ~ 3050
魔法棋盘

魔法棋盘

难度:

标签:

题目描述

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

代码结果

运行时间: 2898 ms, 内存: 257.0 MB


// 思路:由于Java Stream不太适合这种复杂的递归和回溯问题,因此这里依然采用传统方法。

public class MagicResonanceStream {
    private int n, m;
    private char[][] board;
    private int count = 0;

    public int solve(int n, int m, String[] chessboard) {
        this.n = n;
        this.m = m;
        this.board = new char[n][m];
        for (int i = 0; i < n; i++) {
            board[i] = chessboard[i].toCharArray();
        }
        backtrack(0, 0);
        return count;
    }

    private void backtrack(int i, int j) {
        if (i == n) {
            count++;
            return;
        }
        int ni = j == m - 1 ? i + 1 : i;
        int nj = j == m - 1 ? 0 : j + 1;
        if (board[i][j] == '?') {
            board[i][j] = '.';
            backtrack(ni, nj);
            board[i][j] = 'B';
            if (!hasResonance(i, j, 'B')) backtrack(ni, nj);
            board[i][j] = 'R';
            if (!hasResonance(i, j, 'R')) backtrack(ni, nj);
            board[i][j] = '?';
        } else {
            if (!hasResonance(i, j, board[i][j])) backtrack(ni, nj);
        }
    }

    private boolean hasResonance(int i, int j, char piece) {
        for (int k = 0; k < n; k++) {
            if (k != i && board[k][j] != '.' && board[k][j] != '?') {
                if (board[k][j] != piece && Math.abs(k - i) == 2) return true;
            }
        }
        for (int k = 0; k < m; k++) {
            if (k != j && board[i][k] != '.' && board[i][k] != '?') {
                if (board[i][k] != piece && Math.abs(k - j) == 2) return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        MagicResonanceStream solver = new MagicResonanceStream();
        System.out.println(solver.solve(3, 3, new String[]{"..R", "..B", "?R?"})); // 5
        System.out.println(solver.solve(3, 3, new String[]{"?R?", "B?B", "?R?"})); // 105
    }
}

解释

方法:

题解采用动态规划方法来解决问题。首先,如果列数大于行数,交换它们以简化处理。定义不同状态以表示棋盘上的可能配置,并使用一个转换字典来表明从一种状态到另一种状态的可能性。这些状态包括空格、放置黑棋、放置红棋、以及相邻位置上的不同棋子配置。使用一个二维字典dp存储每个位置的所有可能状态及其计数。遍历棋盘的每个格子,根据当前格子的内容(确定棋子、空或待定)更新dp数组。对于每个格子,考虑所有可能的棋子放置(如果当前是'?'则有三种可能),并基于前一个状态(上一个格子或上一行的结束)更新当前状态的计数。在行的开始和结束以及列的转换时需要特别处理。最后,所有在棋盘最后一个位置的状态计数之和即为答案。

时间复杂度:

O(3^(n*m))

空间复杂度:

O(3^(n*m))

代码细节讲解

🦆
动态规划解法中,状态转换字典trans是如何确保棋子之间不会产生魔法共鸣的?具体是如何定义这些状态转换的?
在此题解中,状态转换字典`trans`通过详细地定义每种棋子在特定前状态下能够转换到的后状态来确保棋子之间的逻辑规则得以遵守,从而避免不合理的魔法共鸣现象。例如,状态字典中对于红棋'R'的定义是只能从空状态`s`转换到红棋状态`r`,从黑棋状态`b`转换到红黑相邻状态`br`等,这样的定义确保了棋子放置的合理性和规则的遵守。每种棋子和空格的可能状态转换都是根据棋盘规则事先定义好的,保证了算法的正确性和棋子间的合法互动。
🦆
对于棋盘中的问号`?`,解法中提到可以放置空格、红棋或黑棋,这种方法是否确保了所有可能的放置情况都被考虑到了?有没有可能遗漏某些特殊的放置情况?
解法中提到,对于棋盘上的`?`,可以选择放置空格、红棋或黑棋。这种方法确实覆盖了`?`位置可能的所有放置情况,因为按照题目的设定,棋盘上只有空格、红棋或黑棋这三种可能性。因此,这种处理方式没有遗漏任何特殊的放置情况,确保了对所有可能情况的全面考虑。
🦆
在处理棋盘的边界情况时,如何处理棋盘的第一行和第一列?特别是当棋盘的起始格子是`?`时,初始状态是如何设置的?
在处理棋盘的第一行和第一列时,特别关注了如何初始化棋盘的边界状态。如果是棋盘的起始格子,即第一行第一列的格子,根据题解代码,如果这个格子是`?`,则会考虑所有可能的棋子放置情况,包括空格、红棋和黑棋。对于每种可能的棋子,会初始化一个状态,这个状态代表了从棋盘开始到当前格子可能的状态配置。具体到代码实现,会为每种可能的棋子设置初始的状态映射,并在动态规划表`dp`中记录每种状态的计数。这样的处理确保了从棋盘的第一个格子开始,所有可能的状态都已被考虑和记录,为整个棋盘的状态转换提供了基础。

相关问题