回溯 ——八皇后问题
2018-02-24 18:02
295 查看
八皇后问题:
在8*8的棋盘上,放置8个皇后,使两两之间互不攻击。满足:
1).不在同一列
2).不在同一行
3).不在同一对角线
回溯算法:
确定了解空间的组织结构后,回溯法从开始结点(根结点)出发,以深度优先的方式搜索整个解空间。这个开始结点就成为一个活结点,同时也成为当前的扩展结点。在当前的扩展结点处,搜索向纵深方向移至一个新结点。这个新结点就成为一个新的活结点,并成为当前扩展结点。如果在当前的扩展结点处不能再向纵深方向移动,则当前扩展结点就成为死结点。此时,应往回移动(回溯)至最近的一个活结点处,并使这个活结点成为当前的扩展结点。回溯法即以这种工作方式递归地在解空间中搜索,直至找到所要求的解或解空间中已没有活结点时为止。
回溯算法的基本思想:
从一条路往前走,能进则进,不能进则退回来,换一条路再试。八皇后问题就是回溯算法的典型,第一步按照顺序放一个皇后,然后第二步符合要求放第2个皇后,如果没有位置符合要求,那么就要改变第一个皇后的位置,重新放第2个皇后的位置,直到找到符合条件的位置就可以了。回溯在迷宫搜索中使用很常见,就是这条路走不通,然后返回前一个路口,继续下一条路。回溯算法说白了就是穷举法。不过回溯算法使用剪枝函数,剪去一些不可能到达 最终状态(即答案状态)的节点,从而减少状态空间树节点的生成。回溯法是一个既带有系统性又带有跳跃性的的搜索算法。它在包含问题的所有解的解空间树中,按照深度优先的策略,从根结点出发搜索解空间树。算法搜索至解空间树的任一结点时,总是先判断该结点是否肯定不包含问题的解。如果肯定不包含,则跳过对以该结点为根的子树的系统搜索,逐层向其祖先结点回溯。否则,进入该子树,继续按深度优先的策略进行搜索。回溯法在用来求问题的所有解时,要回溯到根,且根结点的所有子树都已被搜索遍才结束。而回溯法在用来求问题的任一解时,只要搜索到问题的一个解就可以结束。这种以深度优先的方式系统地搜索问题的解的算法称为回溯法,它适用于解一些组合数较大的问题。
每一行一定有一个皇后,因此思考每个应该放在哪一列;
用试探的方法
i为行数 j为列数
q[i] = j 表示第i行上的皇后放在第j列
C[j] = false 说明放好皇后之后当前列不安全
左上到右下的对角线1需要 i - j 为常数;
L[i-j+9] = false 表示在( i , j ) 位置放皇后之后,对角线1不安全。
左下到右上的对角线2需要 i + j 为常数;
R[i+j] = false 表示在( i , j ) 位置放皇后之后,对角线2不安全。
于是得出了在( i , j ) 放皇后之后的安全条件为
C[j]为布尔型,j = 1, 2, … , 8 初始化时全部为true;
L[k]为布尔型,k = i-j+9, k = 2, 3, … , 16 初始化时全部为true;
R[m]为布尔型,m = i+j, k = 2, 3, … , 16 初始化时全部为true;
思路:
1. 放皇后q[i] = j 后,同时让第j列和过( i , j )位置的两条对角线变为不安全;
2. 检查i是否为8,如果为8,表示已经放完8个皇后,这时让方案数Num加1,输出该方案下8个皇后在棋盘上的位置;否则,未到8个,还要让皇后数i加1再试着放,这时还要递归调用Try(i+1)。
3. 为了寻找不同的方案,当一个方案输出之后,要回溯,将先前放的皇后从棋盘上拿起来,看看还有没有可能换一处放置。这时要将被拿起来的皇后的所在位置的第j列和两条对角线恢复为安全的。
代码的解释:以下代码单步执行,Try(1)~Try(5)会先安排第一行到第五行的皇后(第五行的皇后在第4列),之后调用Try(6)
在8*8的棋盘上,放置8个皇后,使两两之间互不攻击。满足:
1).不在同一列
2).不在同一行
3).不在同一对角线
回溯算法:
确定了解空间的组织结构后,回溯法从开始结点(根结点)出发,以深度优先的方式搜索整个解空间。这个开始结点就成为一个活结点,同时也成为当前的扩展结点。在当前的扩展结点处,搜索向纵深方向移至一个新结点。这个新结点就成为一个新的活结点,并成为当前扩展结点。如果在当前的扩展结点处不能再向纵深方向移动,则当前扩展结点就成为死结点。此时,应往回移动(回溯)至最近的一个活结点处,并使这个活结点成为当前的扩展结点。回溯法即以这种工作方式递归地在解空间中搜索,直至找到所要求的解或解空间中已没有活结点时为止。
回溯算法的基本思想:
从一条路往前走,能进则进,不能进则退回来,换一条路再试。八皇后问题就是回溯算法的典型,第一步按照顺序放一个皇后,然后第二步符合要求放第2个皇后,如果没有位置符合要求,那么就要改变第一个皇后的位置,重新放第2个皇后的位置,直到找到符合条件的位置就可以了。回溯在迷宫搜索中使用很常见,就是这条路走不通,然后返回前一个路口,继续下一条路。回溯算法说白了就是穷举法。不过回溯算法使用剪枝函数,剪去一些不可能到达 最终状态(即答案状态)的节点,从而减少状态空间树节点的生成。回溯法是一个既带有系统性又带有跳跃性的的搜索算法。它在包含问题的所有解的解空间树中,按照深度优先的策略,从根结点出发搜索解空间树。算法搜索至解空间树的任一结点时,总是先判断该结点是否肯定不包含问题的解。如果肯定不包含,则跳过对以该结点为根的子树的系统搜索,逐层向其祖先结点回溯。否则,进入该子树,继续按深度优先的策略进行搜索。回溯法在用来求问题的所有解时,要回溯到根,且根结点的所有子树都已被搜索遍才结束。而回溯法在用来求问题的任一解时,只要搜索到问题的一个解就可以结束。这种以深度优先的方式系统地搜索问题的解的算法称为回溯法,它适用于解一些组合数较大的问题。
每一行一定有一个皇后,因此思考每个应该放在哪一列;
用试探的方法
“向前走,碰壁回头”的策略,这就是
“回溯法”的解题思路。
i为行数 j为列数
q[i] = j 表示第i行上的皇后放在第j列
C[j] = false 说明放好皇后之后当前列不安全
左上到右下的对角线1需要 i - j 为常数;
L[i-j+9] = false 表示在( i , j ) 位置放皇后之后,对角线1不安全。
左下到右上的对角线2需要 i + j 为常数;
R[i+j] = false 表示在( i , j ) 位置放皇后之后,对角线2不安全。
于是得出了在( i , j ) 放皇后之后的安全条件为
nq = C[j] && L[i-j+9] && R[i+j]
C[j]为布尔型,j = 1, 2, … , 8 初始化时全部为true;
L[k]为布尔型,k = i-j+9, k = 2, 3, … , 16 初始化时全部为true;
R[m]为布尔型,m = i+j, k = 2, 3, … , 16 初始化时全部为true;
思路:
1. 放皇后q[i] = j 后,同时让第j列和过( i , j )位置的两条对角线变为不安全;
2. 检查i是否为8,如果为8,表示已经放完8个皇后,这时让方案数Num加1,输出该方案下8个皇后在棋盘上的位置;否则,未到8个,还要让皇后数i加1再试着放,这时还要递归调用Try(i+1)。
3. 为了寻找不同的方案,当一个方案输出之后,要回溯,将先前放的皇后从棋盘上拿起来,看看还有没有可能换一处放置。这时要将被拿起来的皇后的所在位置的第j列和两条对角线恢复为安全的。
代码的解释:以下代码单步执行,Try(1)~Try(5)会先安排第一行到第五行的皇后(第五行的皇后在第4列),之后调用Try(6)
(向前走),Try(6)中由于每一列都无法放置皇后,因此Try(6)的for循环结束后什么都没做就结束了。此时回溯到Try(5)
(碰壁回头),Try(5)将之前的i=5,j=4安全标志修改为安全,继续Try(5)的for循环,第五行的皇后位置变为i=5,j=8,调用Try(6),第六列依旧没有位置放皇后,Try(5)的for循环结束,回到Try(4)……
#include <iostream> using namespace std; const int Normalize = 9; int Num; int Q[9]; //记录8个皇后所占用的列号 bool C[9]; //C[1]~C[8],当前列是否安全 bool L[17]; //L[2]~L[16],(i-j)对角线是否安全 bool R[17]; //R[2]~R[16],(i+j)对角线是否安全 void Try(int i) //i为行号 { for(int j = 1; j <= 8; j++) { if(C[j] && L[i - j + Normalize] && R[i + j]) //第i行j列安全 { //第一件事,占用位置(i,j) Q[i] = j; //修改安全标志,包括所在列和两个对角线 C[j] = false; L[i-j+Normalize] = false; R[i+j] = false; //第二件事,判断是否放完8个皇后 if(i < 8) Try(i+1); else { Num++; cout << "方案" << Num << ":"; for(int k = 1; k <= 8; k++) cout << Q[k] << " "; cout << endl; } //第三件事,修改安全标志,回溯 C[j] = true; L[i - j + Normalize] = true; R[i + j] = true; } } } int main() { Num = 0; for(int i = 0; i < 9; i++) C[i] = true; for(int i = 0; i < 17; i++) L[i] = R[i] = true; Try(1); //递归放置8个皇后,从第一个开始放 return 0; }
相关文章推荐
- 八皇后问题 回溯
- 经典回溯问题-八皇后
- 回溯问题:八皇后
- 回溯法经典问题-八皇后
- 八皇后问题 回溯
- C#WPF实现回溯算法解决八皇后问题
- 回溯4--八皇后问题
- 【算法复习二】八皇后问题 ---- 回溯
- 回溯法:八皇后问题
- Java基于循环递归回溯实现八皇后问题算法示例
- 回溯法求八皇后问题
- 八皇后问题 回溯递归 C语言版
- 八皇后问题 回溯
- 八皇后问题回溯算法演示系统
- 【算法复习二】八皇后问题 ---- 回溯
- 八皇后问题——回溯
- 八皇后问题回溯求解
- 八皇后问题 DFS,回溯剪枝 //http://poj.grids.cn/practice/2698
- 回溯法解八皇后问题
- 回溯法——八皇后问题【通俗易懂】