问题描述:在8X8的棋盘上放置八个皇后,任意两个皇后都不能处于同一行、列或者同一斜线上。
分析:每行只能放一个,每列只能放一个。
回溯
如何判段在当前位置是否可以防止皇后
行和列的判断很简单,主要是对角线的判断
在 / 对角线上的点:行 + 列相等
在 \ 对角线上的点:行 - 列相等
通过建立皇后对象的结构体,重载==实现两个皇后相等的判断
bool operator==(const Queen& obj){
return (x == obj.x || y == obj.y ||
(x + y) == (obj.x + obj.y) ||
(x - y) == (obj.x - obj.y));
return x == obj.x;
}
建立Queen表示皇后
包含:坐标,皇后相等的判断
struct Queen
{
int x, y;
Queen(int xx = 0, int yy = 0) :x(xx), y(yy){}
bool operator==(const Queen& obj){
return (x == obj.x || y == obj.y ||
(x + y) == (obj.x + obj.y) ||
(x - y) == (obj.x - obj.y));
return x == obj.x;
}
bool operator!=(const Queen&obj){
return *this == obj;
}
void operator=(const Queen&obj){
x = obj.x;
y = obj.y;
}
};
思路
显而易见,每一行只能放一个皇后,因此我们选择一行一行的防止皇后。我们将可以的满足条件的Queen放入vector中。第一个皇后从(0,0)位置开始放置,并将其信息记录到vector中,然后进入下一行,从(1,0)开始通过在vector中find当前Queen(重载过==)来判断是否是合法位置,可以放置则入栈,不能则进入(1,1),循环进行。。。。
find函数
bool find(vector<Queen>v, Queen obj){
int i = 0;
for (i = v.size()-1; i >=0; i--){
if (v[i] == obj)
return true;;
}
return false;
}
直到这一行都没有位置可以放置的时候,说明上次放置的位置不合理,出vector中剔除最后一个元素(等价于出栈),回到剔除元素的位置,换下一个位置判断。。。
Queen pop(vector<Queen>&v){
Queen q;
q = v[v.size()-1];
v.pop_back();
return q;
}
当成功放置八个皇后后,同样出栈回到上次的位置,换下一个位置判断,这样就可以得到所有的可能。
void placeQueens(int N){
vector<Queen>v;
Queen q(0, 0);
do
{
if (N <= v.size() || N <= q.y){//非法点 出栈
//若size==N说明已经找出一组解了 把当前栈顶同样出栈 找下一组解
q = pop(v);
q.y++;
}
else
{
while ((q.y < N) && (find(v, q)))
退出while的可能:
//1.q.y>=N跳出边界,(跳出边界前没有找到不相同的 )
//出现这中情况则说明在y<N的范围内没有合法点 所以上一次的点是不合法的 会出栈
//2.q.y<N在边界中合法,则只有后面的条件为false 即和前面的点都不同
// 这种点就是当前的合法点,入栈。
{
q.y++;
}
if (N>q.y){
v.push_back(q);//记录当前合法点入栈信息x y
q.x++;//行向下移动找下一行的合法点
q.y = 0;
}
}
if (v.size() >= N){
print(v, N);
//只有合法点才会入栈 一共有N个合法点 所以当
//size==N时说明栈中的点就是一组解 total++
total++;
cout << total << endl;
}
}
while (0<q.x || q.y<N);
//更容易理解的循环条件是while (!(q.x == 0 && q.y == N));
}
递归
相对而言递归就简单的多,递归本质上相当于操作系统帮我们完成了入栈出栈的操作,因此我们只需要判断递归的开始条件和终止条件即可。
思路:
我们用一个int ret[9]的数组来记录皇后的位置,用数组下标表示皇后所在行,下标中对应元素表示列。
用函数void placeQueens2(int queennum)来实现目标,参数为已经放置的皇后个数,每成功找到一个皇后则在 ret中记录他的位置,然后
void placeQueens2(queennum+1)找下一个皇后
当下一个皇后找不到时 返回,并从ret中将ret[–queennum]的皇后信息清除(因为不可用),return递归终止条件1
当queennum = 7时说明8个皇后都找到了,递归终止条件2
int ret[8] = { -1 };
int q_total = 0;
bool avaiable(int num, int pos){
for (int i = 0; i < num; ++i){
if (ret[i] == pos || (i + ret[i] == num + pos) ||
(i - ret[i] == num - pos))
return false;
}
return true;
}
void print(int ret[]){
for (int i = 0; i < 8;i++){
cout << ret[i] << " ";
}
cout << endl;
}
void placeQueens2(int queennum){
for (int i = 0; i < 8; i++){
if (avaiable(queennum, i)){
ret[queennum] = i;
if (queennum<7)
placeQueens2(queennum + 1);
else{
q_total++;
print(ret);
break;//这里用break return 甚至什么都不用结果都一样
//但是过程是不同的用break效率最高
}
}
}
ret[--queennum] = -1;//用--queennum也可以,因为只要对皇后个数--后,
//其内容就已经失效了
return;
}