nクイーン問題は、n×nチェス盤にnクイーンを配置し、クイーンを互いに攻撃できないようにする方法を研究します。
整数nを指定すると、すべての異なるnクイーン問題の解を返します。
各ソリューションには、n-クイーン問題の明確なポーン配置計画が含まれています。ここで、「Q」と「。」はそれぞれクイーンと空の位置を表します。
例:
入力:4
出力:[
[
".Q …"、//ソリューション1 “…Q”、
“ Q…”、
“…Q。”]、
["…Q。"、//解決策2
“ Q…”、
“…Q”、
“ .Q…”]
]
説明:4クイーン問題には2つの異なる解決策があります。
解決策:バックトラッキングメソッドを使用してすべての可能な状況をトラバースし、要件を満たす状況を見つけます。時間の複雑さを軽減するために、トラバース中の条件付き制限を増やすことができます。クイーンを配置するときはいつでも、左スラッシュ、右スラッシュ、垂直方向および水平方向を「禁止ゾーン」として設定できます。つまり、クイーンの位置を配置できません。同時に、バックトラックの便宜上、クイーンを水平方向に上から下に、各行に配置します1を置く(nクイーンがn ∗ *に置かれるため)∗ nはチェス盤にあるため、各列に1つずつ配置する必要があります)。
import java.util.*;
class Solution {
private final Stack<Integer>stack=new Stack<>();
private final List<List<String>>res=new ArrayList<>();
public List<List<String>> solveNQueens(int n) {
//二维布尔型数组记录不能放置皇后的位置
boolean[][] banned=new boolean[n][n];
DFS(banned,0);
return res;
}
private void DFS(boolean[][]banned,int row){
if(stack.size()== banned.length){
getMap(stack,res);
return;
}
Queue<int[]>save=new LinkedList<>();
int len=banned[row].length;
//在每一行从左到右一次尝试放皇后
for(int i=0;i<len;i++){
if(!banned[row][i]) {
stack.add(i);
//将该行往下的每一行不能放置皇后的位置都设置为true
for (int j = row; j < banned.length; j++) {
if(!banned[j][i ]) {
save.add(new int[]{
j, i});
banned[j][i] = true;
}
int k = j - row;
if (i + k < len&&!banned[j][i + k]) {
save.add(new int[]{
j, i + k});
banned[j][i + k] = true;
}
if (i - k >= 0&&!banned[j][i - k]) {
save.add(new int[]{
j, i - k});
banned[j][i - k] = true;
}
}
DFS(banned, row + 1);
stack.pop();
//还原
while(!save.isEmpty()){
int[] temp=save.poll();
banned[temp[0]][temp[1]]=false;
}
}
}
}
private void getMap(Stack<Integer>stack,List<List<String>>res){
int size=stack.size();
List<String>map=new ArrayList<>();
for(int x:stack){
StringBuilder row=new StringBuilder();
for(int i=0;i<size;i++){
if(i!=x)
row.append(".");
else
row.append("Q");
}
map.add(row.toString());
}
res.add(map);
}
}
最適化:チェス盤のルールを使用してコードを最適化できます。実際、チェス盤のすべてのスラッシュには、そのスラッシュの一意の番号があります。たとえば、同じ右スラッシュのポイントの水平座標と垂直座標の差は一意の定数です、左の斜線上の点の水平座標と垂直座標の合計は一意の定数であり、異なる列は列ラベルの番号で区別できます。したがって、クイーンに配置できないポイントを格納するために2次元配列を使用する必要がなくなり、直接数値を使用しますクイーンを特定の対角線または特定の列に配置できないことを示します。
import java.util.*;
class Solution {
private final Stack<Integer>stack=new Stack<>();
private final List<List<String>>res=new ArrayList<>();
private final Set<Integer>left=new HashSet<>();
private final Set<Integer>right=new HashSet<>();
private final Set<Integer>down=new HashSet<>();
public List<List<String>> solveNQueens(int n) {
boolean[][] banned=new boolean[n][n];
backTrace(0,n);
return res;
}
private void backTrace(int column,int len){
if(stack.size()==len){
getMap(stack,res);
return;
}
for(int i=0;i<len;i++){
int rightKey=column-i;
int leftKey=column+i;
if(!right.contains(rightKey)&&!left.contains(leftKey)&&!down.contains(i)) {
right.add(rightKey);
left.add(leftKey);
down.add(i);
stack.push(i);
backTrace(column+1,len);
right.remove(rightKey);
left.remove(leftKey);
down.remove(i);
stack.pop();
}
}
}
private void getMap(Stack<Integer>stack,List<List<String>>res){
int size=stack.size();
List<String>map=new ArrayList<>();
for(int x:stack){
StringBuilder row=new StringBuilder();
for(int i=0;i<size;i++){
if(i!=x)
row.append(".");
else
row.append("Q");
}
map.add(row.toString());
}
res.add(map);
}
}