问题描述
八皇后问题是一个古老而著名的问题,它是回溯算法的典型例题,现在用分支限界的算法来解决这个问题。该问题是十九世纪德国著名数学家高斯于1850年提出的:在8行8列的国际象棋棋盘上摆放着 八个皇后。若两个皇后位于同一行、同一列或同一对角线上,则称为它们为互相攻击。现在要求使这N个皇后不能相互攻击,即任意两个皇后都不能处于同一行、同一列或同一对角线上,问有多少种摆法。
在分支限界法中,每一个活结点只有一次机会成为扩展节点。活结点一旦成为扩展节点,就一次性产出所有儿子节点。在这些所有的儿子节点中,导致不可行解或导致非最优解的儿子节点被舍弃,满足条件的儿子结点被加入到活结点表中。此后,从活结点表中取下一个结点成为当前扩展节点,并重复上述结点扩展过程。这个过程一直持续到我们找到所需的解或者是活性表为空为止。
算法模板
定义根节点t0为初始化扩展节点
根节点t0入队
while(队不空)
{
出队 --t;
for (j = r0; j < rn; ++j)
{
对t利用规则j发展新节点tj;
利用限界函数判断tj是否可行;
if (可行)
{
若是目标解,找到结果,return
否则,进队
}
}
}
#include <iostream>
#include <queue>
using namespace std;
class Node
{
public:
Node(int n) : t(0), n(n)
{
pos = new int[n + 1];
for (int i = 0; i <= n; ++i)
{
pos[i] = 0;
}
}
Node(const Node& other)
{
t = other.t;
n = other.n;
pos = new int[other.n + 1];
for (int i = 0; i <= n; ++i)
{
pos[i] = other.pos[i];
}
}
~Node()
{
if (pos != NULL)
{
delete[] pos;
pos = NULL;
}
}
bool check(int next);
int t;//当前已经放置了多少个皇后
int n;//需要放置多少个皇后
int *pos; //指向当前已放好的皇后位置,pos[1]代表第1个皇后所放的列数,所放的行数为1,这样设置可以不用检查行是否相等
};
bool Node::check(int next)
{
int i; //表示已经已经放置皇后的行
for (i = 1; i <= t; ++i)
{
int j = pos[i]; //代表已经放置的皇后的列
//同列
if (j == next)
{
return false;
}
//右上角到左下角的对角线
if ((next - j) == (i - 1 - t))
{
return false;
}
//左上角到右下角的对角线
if ((next - j) == (t + 1 - i))
{
return false;
}
}
return true;
}
class Queen
{
public:
Queen(int x) : n(x), ansNum(0){}
int QueenArrange(); //排列皇后的方式
private:
int n; //皇后数量
int ansNum; //n皇后解的数量
};
int Queen::QueenArrange()
{
queue<Node> q;
Node f(n);
q.push(f);
while (!q.empty())
{
Node x = q.front();
q.pop();
if (x.t == n)
{
++ansNum;
}
//一次性将当前节点的所有扩展节点考虑完,符合条件的插入队列
for (int i = 1; i <= n; ++i)
{
//利用剪枝函数,将不符合条件的分支切掉
if (x.check(i))
{
Node child(x);
++child.t;
child.pos[child.t] = i; //记录位置
q.push(child);
}
}
}
return ansNum;
}
int main()
{
int n = 8;
Queen queen(8);
cout << queen.QueenArrange() << endl;
}