N皇后1
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数 n ,返回所有不同的 n 皇后问题 的解决方案。
每一种解法包含一个不同的 n 皇后问题 的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
示例 1:
输入:n = 4
输出:[[".Q…","…Q",“Q…”,"…Q."],["…Q.",“Q…”,"…Q",".Q…"]]
解释:如上图所示,4 皇后问题存在两个不同的解法。
示例 2:
输入:n = 1
输出:[[“Q”]]
回溯
class Solution {
public List<List<String>> solveNQueens(int n) {
char[][] board = new char[n][n];
for (int i = 0; i < n; i++)
Arrays.fill(board[i], '.');
List<List<String>> result = new ArrayList<>();
backtrack(result, board, 0, n);
return result;
}
private void backtrack(List<List<String>> result, char[][] board, int row, int n) {
if (row == n) {
List<String> ans = new ArrayList<>();
for (int i = 0; i < n; i++)
ans.add(new String(board[i]));
result.add(ans);
return;
}
for (int j = 0; j < n; j++) {
if (valid(board, row, j, n)) {
board[row][j] = 'Q';
backtrack(result, board, row + 1, n);
board[row][j] = '.';
}
}
}
private boolean valid(char[][] board, int row, int col, int n) {
for (int i = 0; i < row; i++) {
if (board[i][col] == 'Q')
return false;
}
for (int j = 0; j < col; j++) {
if (board[row][j] == 'Q')
return false;
}
for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) {
if (board[i][j] == 'Q')
return false;
}
for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) {
if (board[i][j] == 'Q')
return false;
}
return true;
}
}
回溯2
用数组存储状态,可以减少遍历。
class Solution {
private boolean[] columns;
private boolean[] diagonal1;
private boolean[] diagonal2;
public List<List<String>> solveNQueens(int n) {
columns = new boolean[n];
diagonal1 = new boolean[2 * n - 1];
diagonal2 = new boolean[2 * n - 1];
char[][] board = new char[n][n];
for (int i = 0; i < n; i++)
Arrays.fill(board[i], '.');
List<List<String>> result = new ArrayList<>();
backtrack(result, board, 0, n);
return result;
}
private void backtrack(List<List<String>> result, char[][] board, int r, int n) {
if (r == n) {
List<String> ans = new ArrayList<>();
for (int i = 0; i < n; i++)
ans.add(new String(board[i]));
result.add(ans);
return;
}
for (int j = 0; j < n; j++) {
if (valid(r, j, n)) {
setValid(r, j, n);
board[r][j] = 'Q';
backtrack(result, board, r + 1, n);
board[r][j] = '.';
setValid(r, j, n);
}
}
}
private void setValid(int r, int c, int n) {
columns[c] = !columns[c];
diagonal1[r + c] = !diagonal1[r + c];
diagonal2[r -c + n - 1] = !diagonal2[r - c + n - 1];
}
private boolean valid(int r, int c, int n) {
return !(columns[c] | diagonal1[r + c] | diagonal2[r - c + n - 1]);
}
}
N皇后2
给你一个整数 n ,返回 n 皇后问题 不同的解决方案的数量。
示例 1:
输入:n = 4
输出:2
解释:如上图所示,4 皇后问题存在两个不同的解法。
示例 2:
输入:n = 1
输出:1
回溯
class Solution {
private boolean[] columns;
private boolean[] diagonal1;
private boolean[] diagonal2;
private int count = 0;
public int totalNQueens(int n) {
columns = new boolean[n];
diagonal1 = new boolean[2 * n - 1];
diagonal2 = new boolean[2 * n - 1];
char[][] board = new char[n][n];
for (int i = 0; i < n; i++)
Arrays.fill(board[i], '.');
backtrack(board, 0, n);
return count;
}
private void backtrack(char[][] board, int r, int n) {
if (r == n) {
count++;
return;
}
for (int j = 0; j < n; j++) {
if (valid(r, j, n)) {
setValid(r, j, n);
board[r][j] = 'Q';
backtrack(board, r + 1, n);
board[r][j] = '.';
setValid(r, j, n);
}
}
}
private void setValid(int r, int c, int n) {
columns[c] = !columns[c];
diagonal1[r + c] = !diagonal1[r + c];
diagonal2[r -c + n - 1] = !diagonal2[r - c + n - 1];
}
private boolean valid(int r, int c, int n) {
return !(columns[c] | diagonal1[r + c] | diagonal2[r - c + n - 1]);
}
}
基于位运算的回溯
class Solution {
public int totalNQueens(int n) {
return solve(n, 0, 0, 0, 0);
}
public int solve(int n, int row, int columns, int diagonals1, int diagonals2) {
if (row == n) {
return 1;
} else {
int count = 0;
int availablePositions = ((1 << n) - 1) & (~(columns | diagonals1 | diagonals2));
while (availablePositions != 0) {
int position = availablePositions & (-availablePositions);
availablePositions = availablePositions & (availablePositions - 1);
count += solve(n, row + 1, columns | position, (diagonals1 | position) << 1, (diagonals2 | position) >> 1);
}
return count;
}
}
}
金字塔转换矩阵
现在,我们用一些方块来堆砌一个金字塔。 每个方块用仅包含一个字母的字符串表示。
使用三元组表示金字塔的堆砌规则如下:
- 对于三元组 ABC ,C 为顶层方块,方块 A 、B 分别作为方块 C 下一层的的左、右子块。当且仅当 ABC 是被允许的三元组,我们才可以将其堆砌上。
- 初始时,给定金字塔的基层 bottom,用一个字符串表示。一个允许的三元组列表 allowed,每个三元组用一个长度为 3 的字符串表示。
- 如果可以由基层一直堆到塔尖就返回 true ,否则返回 false 。
示例 1:
输入:bottom = “BCD”, allowed = [“BCG”, “CDE”, “GEA”, “FFF”]
输出:true
解释:
可以堆砌成这样的金字塔:
因为符合 BCG、CDE 和 GEA 三种规则。
示例 2:
输入:bottom = “AABA”, allowed = [“AAA”, “AAB”, “ABA”, “ABB”, “BAC”]
输出:false
解释:
无法一直堆到塔尖。
注意, 允许存在像 ABC 和 ABD 这样的三元组,其中 C != D。
回溯+哈希表
class Solution {
private Map<String, List<Character>> map;
public boolean pyramidTransition(String bottom, List<String> allowed) {
map = new HashMap<>();
for (String s : allowed) {
String key = s.substring(0, 2);
List<Character> list = map.getOrDefault(key, new ArrayList<Character>());
list.add(s.charAt(2));
map.put(key, list);
}
return dfs(bottom, new StringBuilder(), 0);
}
private boolean dfs(String bottom, StringBuilder next, int start) {
if (bottom.length() == 1)
return true;
if (next.length() + 1 == bottom.length())
return dfs(next.toString(), new StringBuilder(), 0);
String key = bottom.substring(start, start + 2);
for (char c : map.getOrDefault(key, new ArrayList<Character>())) {
next.append(c);
if (dfs(bottom, next, start + 1))
return true;
next.deleteCharAt(next.length() - 1);
}
return false;
}
}
回溯+位运算存储
class Solution {
private int[][] map = new int[7][7];
public boolean pyramidTransition(String bottom, List<String> allowed) {
for (String s : allowed) {
int a = s.charAt(0) - 'A', b = s.charAt(1) - 'A', c = s.charAt(2) - 'A';
map[a][b] |= (1 << c);
}
List<Integer> nums = new ArrayList<>();
for (int i = 0; i < bottom.length(); i++)
nums.add(bottom.charAt(i) - 'A');
return dfs(nums, new ArrayList<>(), 0);
}
private boolean dfs(List<Integer> bottom, List<Integer> next, int start) {
if (bottom.size() == 1)
return true;
if (next.size() + 1 == bottom.size())
return dfs(next, new ArrayList<>(), 0);
int a = bottom.get(start), b = bottom.get(start + 1), c = map[a][b];
for (int i = 0; i < 7; i++) {
if ((1 << i & c) == 0)
continue;
next.add(i);
if (dfs(bottom, next, start + 1))
return true;
next.remove(next.size() - 1);
}
return false;
}
}