トピック
- バックトラック
説明
https://leetcode.com/problems/n-queens/
N-クイーンズパズル配置の問題であるn
にクイーンをn x n
全くクイーンが互いに攻撃しないようなチェス盤。
整数が与えられた場合n
、すべての異なる解をn-queensパズルに返します。
各ソリューションには、n-クイーンの配置の個別のボード構成が含まれています。ここで'Q'
、および'.'
両方は、それぞれクイーンと空のスペースを示します。
例1:
Input: n = 4
Output: [[".Q..","...Q","Q...","..Q."],["..Q.","Q...","...Q",".Q.."]]
Explanation: There exist two distinct solutions to the 4-queens puzzle as shown above
例2:
Input: n = 1
Output: [["Q"]]
制約:
1 <= n <= 9
分析
トピック分析
nクイーン問題は、バックトラッキングアルゴリズムによって解決される古典的な問題です。
まず、女王の制約を見てください。
- 旅行できない
- 同じ列に含めることはできません
- 同じスラッシュにすることはできません
制約を決定したら、クイーンの位置を検索する方法を見てみましょう。実際、クイーンの位置の検索はツリーとして抽象化できます。
次の図に示すように、3 * 3のチェスカードを使用して、検索プロセスをツリーとして抽象化します。
理解を容易にするために、クイーンが「同じスラッシュになることはできない」という制限が一時的にブロックされていることに注意してください。
この図から、2次元マトリックスのマトリックスの高さはツリーの高さであり、マトリックスの幅はツリー構造の各ノードの幅(子の数)であることがわかります。ある)。
次に、クイーンの制約を使用してツリーを逆方向に検索します。ツリーのリーフノードが検索されている限り、クイーンの適切な位置が見つかりました。
3つの車線に戻る
古典的な遡及的テンプレートに従ったプログラミング:
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
関数シグネチャ
List<String> rows
:現在の走査線の前の女王振り子。int lengthOfSide
:チェス盤の片側、つまりクイーンNの北にいくつの正方形がありますか。int depth
:現在の再帰の深さlengthOfSide
。最大値はです。List<List<String>> result
:ストレージは、Nクイーンの配置方法の要件を満たしています。
コードは次のように表示されます。
private void backstracking(List<String> rows, int lengthOfSide, int depth, List<List<String>> result) {
}
終了条件
トピック分析の例の図を振り返ると、ボードの下部(つまり、リーフノード)に再帰すると、結果を収集して返すことができます。
コードは次のように表示されます。
if (rows.size() == lengthOfSide) {
result.add(new ArrayList<>(rows));
return;
}
トラバーサルプロセス
まず、クイーン振り子法が合法であるかどうか、クイーンが一緒に行くことができないかどうか、同じ列、同じスラッシュ、コードは次のとおりであるかどうかを確認します。
private boolean isValid(List<String> rows, int row, int col, int lengthOfSide) {
// 上方向
for (int i = row - 1; i > -1; i--) {
if (rows.get(i).charAt(col) == 'Q')
return false;
}
// 左上斜线方向
for (int i = row - 1, j = col - 1; i > -1 && j > -1; i--, j--) {
if (rows.get(i).charAt(j) == 'Q')
return false;
}
// 右上斜线方向
for (int i = row - 1, j = col + 1; i > -1 && j < lengthOfSide; i--, j++) {
if (rows.get(i).charAt(j) == 'Q')
return false;
}
return true;
}
次に、クイーン文字列を含む行を生成します。コードは次のとおりです。
private String makeRow(int index, int lengthOfSide) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < lengthOfSide; i++) {
sb.append(i == index ? 'Q' : '.');
}
return sb.toString();
}
最後に、トラバーサルプロセス:
for (int i = 0; i < lengthOfSide; i++) {
if (isValid(rows, depth, i, lengthOfSide)) {
//检测皇后放置第depth行,第i列,是否合法
rows.add(makeRow(i, lengthOfSide));//放置符合要求皇后所在字符串行
backstracking(rows, lengthOfSide, depth + 1, result);
rows.remove(rows.size() - 1);//回溯
}
}
最終コード
提出に移動してください。
参照
提出
import java.util.ArrayList;
import java.util.List;
public class NQueens {
public List<List<String>> solveNQueens(int n) {
List<List<String>> result = new ArrayList<>();
List<String> rows = new ArrayList<>();
backstracking(rows, n, 0, result);
return result;
}
private void backstracking(List<String> rows, int lengthOfSide, int depth, List<List<String>> result) {
if (rows.size() == lengthOfSide) {
result.add(new ArrayList<>(rows));
return;
}
for (int i = 0; i < lengthOfSide; i++) {
if (isValid(rows, depth, i, lengthOfSide)) {
rows.add(makeRow(i, lengthOfSide));
backstracking(rows, lengthOfSide, depth + 1, result);
rows.remove(rows.size() - 1);
}
}
}
private String makeRow(int index, int lengthOfSide) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < lengthOfSide; i++) {
sb.append(i == index ? 'Q' : '.');
}
return sb.toString();
}
private boolean isValid(List<String> rows, int row, int col, int lengthOfSide) {
// 上方向
for (int i = row - 1; i > -1; i--) {
if (rows.get(i).charAt(col) == 'Q')
return false;
}
// 左上方向
for (int i = row - 1, j = col - 1; i > -1 && j > -1; i--, j--) {
if (rows.get(i).charAt(j) == 'Q')
return false;
}
// 右上方向
for (int i = row - 1, j = col + 1; i > -1 && j < lengthOfSide; i--, j++) {
if (rows.get(i).charAt(j) == 'Q')
return false;
}
return true;
}
}
テスト
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
import java.util.Arrays;
import org.hamcrest.collection.IsEmptyCollection;
import org.junit.Test;
public class NQueensTest {
@Test
public void test() {
NQueens obj = new NQueens();
assertThat(obj.solveNQueens(1), is(Arrays.asList(Arrays.asList("Q"))));
assertThat(obj.solveNQueens(3), IsEmptyCollection.empty());
assertThat(obj.solveNQueens(4), is(Arrays.asList(Arrays.asList(".Q..","...Q","Q...","..Q."), //
Arrays.asList("..Q.","Q...","...Q",".Q.."))));
}
}