原题 https://oj.leetcode.com/problems/n-queens/
这个题写了好久。。。
其实思路比较简单。在每一列上按顺序每一个位置放皇后,而前面已经放好的皇后位置对后面放的皇后的位置是有影响的,因此以后选择摆放位置时要遵守前面的约束。
流程基本是:先在第一列选择一个位置放皇后,然后在第二列选择位置时不能和第一列的皇后有冲突(在一条直线或对角线上),在第三列时不能与一二列有冲突,以此类推,直到找到最后一列符合条件的位置,结果记录下来,然后回溯找其他符合条件的位置;或者在第i列时发现没有任何一个位置符合条件,于是需要回溯到第i-1列去,在该列上换下一个位置,继续以上流程。
整个流程类似于深度优先搜索。用栈保存访问过的位置信息,实现回溯。
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Stack;
public class Solution {
private final char INIT = '#';
public static void main(String[] a)
{
for(String[] ss :new Solution().solveNQueens(5))
{
for(String s : ss)
System.out.println(s);
System.out.println();
}
}
public List<String[]> solveNQueens(int n) {
List<String[]> ll = new LinkedList<String[]>();
char[][][] charsOnStep = new char[n+1][n][n];
charsOnStep[0] = this.getChars(n);
int pre = -1; //上一步是第几步
Stack<int[]> steps = new Stack<int[]>(); //用行,列值对来表示步骤
steps.push(new int[]{0,0});
pre = -1;
charsOnStep[1] = this.putQ(0, 0, charsOnStep[0]);
while(!steps.isEmpty())
{
int[] head = steps.peek();
int r,c;
if(pre >= head[1]) //回溯
{
r = head[0]+1; //回溯时,要接着上次访问的行数+1的位置
c = head[1];
steps.pop();
}
else
{
r = 0;
c = head[1]+1;
}
int i =c;
for(;i<n; i++) //列
{
boolean found = false;
pre = i;
for(int j = r;j<n; j++) //行
{
char[][] temp = charsOnStep[i];
if(temp[j][i] == '#')
{
charsOnStep[i+1] = putQ(j,i,temp);
steps.push(new int[]{j,i});
found = true;
r = 0; //这里需要把r重置为0,使下一列继续从0开始扫描
break;
}
}
if(!found) //没找到,说明走到i列时不能走下去了,需要回溯
{
break;
}
}
if(i == n) //已经走到最后了,记录结果,然后该回溯了
{
pre = i;
ll.add(ctoss(charsOnStep[n]));
}
}
return ll;
}
//用一个二维的字符数组构造字符串数组
private String[] ctoss(char[][] cs)
{
String[] re = new String[cs.length];
for(int i = 0;i<cs.length; i++)
re[i] = new String(cs[i]);
return re;
}
//在r行c列上放置Q,并依据此Q的位置更新其直线斜线上的点为'.'
public char[][] putQ(int r, int c, char[][] cs)
{
cs = copy(cs);
cs[r][c] = 'Q';
for(int i = 0;i<cs.length; i++)
if(i != r)
cs[i][c] = '.';
for(int i = 1;i < cs.length-c;i++)
{
cs[r][c+i] = '.';
if(r+i < cs.length)
cs[r+i][c+i] = '.';
if(r-i >= 0)
cs[r-i][c+i] = '.';
}
return cs;
}
public char[][] copy(char[][] cs)
{
char[][] c = new char[cs.length][cs.length];
for(int i = 0;i<cs.length; i++)
for(int j = 0;j<cs.length; j++)
c[i][j] = cs[i][j];
return c;
}
//初始化一个字符的二维数组,初始值用#标记
public char[][] getChars(int n)
{
char[][] cs = new char[n][n];
for(int i = 0;i<n; i++)
for(int j = 0;j<n; j++)
cs[i][j] = INIT;
return cs;
}
}