回溯算法
回溯法是一种组织搜索的一般技术,有“通用的解题法”之称,用它可以系统的搜索一个问题的所有解或任一解。
应用回溯法求解时,需要明确定义问题的解空间。问题的解空间应至少包含问题的一个(最优)解。
在生成解空间树时,定义以下几个相关概念:
活结点:如果已生成一个结点而它的所有儿子结点还没有全部生成,则这个结点叫做活结点。
扩展结点:当前正在生成其儿子结点的活结点叫扩展结点(正扩展的结点)。
死结点:不再进一步扩展或者其儿子结点已全部生成的结点就是死结点。
在确定了解空间的组织结构后,回溯从开始结点(根结点)出发,以深度优先的方式搜索整个解空间。
这个开始结点成为一个活结点,同时成为当前的扩展结点。在当前的扩展结点,搜索向深度方向进入一个新的结点。这个新结点成为一个新的活结点,并成为当前的扩展结点。
若在当前扩展结点处不能再向深度方向移动,则当前的扩展结点成为死结点,即该活结点成为死结点。此时回溯到最近的一个活结点处,并使得这个活结点成为当前的扩展结点。
回溯法以这样的方式递归搜索整个解空间(树),直至满足中止条件。
N皇后问题
在n×n格的棋盘上放置彼此不受攻击的n个皇后。
按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。n皇后问题等价于在n×n格的棋盘上放置n个皇后,任何两个皇后不放在同一行或同一列或同一斜线上。
编程要求:找出一个n×n格的棋盘上放置n个皇后并使其不能互相攻击的所有方案。(即不能出现在同一行、同一列、同一斜线上的棋子)
#include<iostream>
using namespace std;
#define NUM 20
int n;
int x[NUM];
int sum;
inline bool Place(int t)//内联函数
{
int i;
for (i = 1; i < t; i++)
if ((abs(t - i) == abs(x[i] - x[t])) || (x[i] == x[t]))
return false;
return true;
}
void Backtrack(int t)
{
int i;
//到达叶子结点,获得一个可行方案。累计总数,并输出该方案
if (t > n)
{
sum++; //是全局变量
for (i = 1; i <= n; i++)
cout << x[i];
cout << "\n";
}
else
for (i = 1; i <= n; i++)
{
x[t] = i;
if (Place(t)) Backtrack(t + 1);
}
}
int main()
{
cin >> n;
Backtrack(1);
cout << sum << endl;
}