您的位置:首页 > 其它

回溯算法与N皇后问题

2014-11-15 21:07 435 查看
引言

寻找问题的解的一种可靠的方法是首先列出所有候选解,然后依次检查每一个,在检查完所有或部分候选解后,即可找到所需要的解。理论上,当候选解数量有限并且通过检查所有或部分候选解能够得到所需解时,上述方法是可行的。不过,在实际应用中,很少使用这种方法,因为候选解的数量通常都非常大(比如指数级,甚至是大数阶乘),即便采用最快的计算机也只能解决规模很小的问题。对候选解进行系统检查的方法有多种,其中回溯和分枝定界法是比较常用的两种方法。按照这两种方法对候选解进行系统检查通常会使问题的求解时间大大减少(无论对于最坏情形还是对于一般情形)。事实上,这些方法可以使我们避免对很大的候选解集合进行检查,同时能够保证算法运行结束时可以找到所需要的解。因此,这些方法通常能够用来求解规模很大的问题。

算法思想

回溯(backtracking)是一种系统地搜索问题解答的方法。为了实现回溯,首先需要为问题定义一个解空间(solution space),这个空间必须至少包含问题的一个解(可能是最优的)。

下一步是组织解空间以便它能被容易地搜索。典型的组织方法是图(迷宫问题)或树(N皇后问题)。

一旦定义了解空间的组织方法,这个空间即可按深度优先的方法从开始节点进行搜索。
回溯方法的步骤如下:

1) 定义一个解空间,它包含问题的解。

2) 用适于搜索的方式组织该空间。

3) 用深度优先法搜索该空间,利用限界函数避免移动到不可能产生解的子空间。

回溯算法的一个有趣的特性是在搜索执行的同时产生解空间。在搜索期间的任何时刻,仅保留从开始节点到当前节点的路径。因此,回溯算法的空间需求为O(从开始节点起最长路径的长度)。这个特性非常重要,因为解空间的大小通常是最长路径长度的指数或阶乘。所以如果要存储全部解空间的话,再多的空间也不够用。

算法的应用:N皇后问题


N-Queens




The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two queens attack each other.

Given an integer n, return all distinct solutions to the n-queens puzzle.

Each solution contains a distinct board configuration of the n-queens' placement, where
'Q'
and
'.'
both
indicate a queen and an empty space respectively.

For example,

There exist two distinct solutions to the 4-queens puzzle:
[
[".Q..",  // Solution 1
"...Q",
"Q...",
"..Q."],

["..Q.",  // Solution 2
"Q...",
"...Q",
".Q.."]
]

在一个N*N的棋盘上放置N个皇后,且使得每两个之间不能互相攻击,也就是使得每两个不在同一行,同一列和同一斜角线上。
对于N=1,问题的解很简单,而且我们很容易看出对于N=2和N=3来说,这个问题是无解的。所让我们考虑4皇后问题并用回溯法对它求解。因为每个皇后都必须分别占据—行,我们需要做的不过是为图1棋盘上的每个皇后分配一列。

我们从空棋盘开始,然后把皇后1放到它所在行的第一个可能位置上,也就是第一行第—列。对于皇后2,在经过第一列和第二列的失败尝试之后,我们把它放在第一个可能的位置,就是格子〔2,3),位于第二行第三列的格子。这被证明是一个死胡同,因为皇后:将没有位置可放。所以,该算法进行回溯,把皇后2放在下一个可能位置(2,4)上。然后皇后3就可以放在(3,2),这被证明是另一个死胡同。该算法然后就回溯到底,把皇后1移到(1,2)。接着皇后2到(2,4),皇后3到(3,1),而皇后4到(4,3),这就是该问题的一个解。

代码如下:

public class Solution {
private String[] drawChessboard(ArrayList<Integer> cols) {
String[] chessboard = new String[cols.size()];
for (int i = 0; i < cols.size(); i++) {
chessboard[i] = "";
for (int j = 0; j < cols.size(); j++) {
if (j == cols.get(i)) {
chessboard[i] += "Q";
} else {
chessboard[i] += ".";
}
}
}

return chessboard;
}

private boolean isValid(ArrayList<Integer> cols, int col) {
int row = cols.size();
for (int i = 0; i < row; i++) {
// same column
if (cols.get(i)== col)  {
return false;
}
// left-top to right-bottom
if (i - cols.get(i) == row - col) {
return false;
}
// right-top to left-bottom
if (i + cols.get(i) == row + col) {
return false;
}
}
return true;
}

private void search(int n, ArrayList<Integer> cols, ArrayList<String[]> result) {
if (cols.size() == n) {
result.add(drawChessboard(cols));
return;
}

for (int col = 0; col < n; col++) {
if (!isValid(cols, col)) {
continue;
}
cols.add(col);
search(n, cols, result);
cols.remove(cols.size() - 1);
}
}

public ArrayList<String[]> solveNQueens(int n) {
ArrayList<String[]> result = new ArrayList<String[]>();
if (n <= 0) {
return result;
}
search(n, new ArrayList<Integer>(), result);
return result;
}
}


[/code]
C++版本:

测试皇后冲突的函数:isValid();基本思路:,使用a
记录N皇后位置。根据皇后放置规则可知,每一行有且只有一个皇后,所以从第一行到第N行,依次放置第n个皇后。a
记录第n个皇后所在列数。即第i个皇后放置在第i行第a[i]列。
class Solution {
public:
vector<vector<string> > re;

//测试在第row行,第col列放置皇后是否有效
int isValid(int *a, int n, int row, int col)
{
int tmpcol=0;
for(int tmprow=0;tmprow<row;tmprow++)
{
tmpcol = a[tmprow];
if(tmpcol == col)// 同列
return 0;
if((tmpcol-col) == (tmprow - row))// 在同一右斜线
return 0;
if((tmpcol-col) == (row - tmprow))// 在同一左斜线
return 0;
}
return 1;
}

void PrintN(int *a, int n)
{
vector<string> tmps;
for(int i=0;i<n;i++)
{
string s(n,'.');
s[a[i]]='Q';
tmps.push_back(s);
}
re.push_back(tmps);
}
void n_queens(int *a,int n, int index)
{
for(int i=0;i<n;i++)
{
if(isValid(a,n,index,i))
{
a[index]=i;
if(index == n-1)
{
PrintN(a,n);
//a[index]=0;
return;
}
n_queens(a,n,index+1);
//a[index]=0;
}
}
}

vector<vector<string> > solveNQueens(int n) {

int *a = new int
;
//memset(a,0,sizeof(int)*n);
n_queens(a,n,0);
return re;
}
};






内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: