回溯法(8皇后问题)
递归函数不再调用它本身,而是返回上一层调用,这种现象称为回溯。
表现在解答树中就是一个结点本来应该有的分支因为不满足条件而没有接续产生分支。
八皇后问题:在8*8的棋盘上,放置8个皇后,使其不互相攻击,皇后的攻击范围为同行同列和同对角线,找出所有解。
思考可知:每一行只能放一个,每一列也只能放一个。
用c[i]表示第i行的皇后的列数,行数、列数范围都是0~7。
问题变成了0~7的排列问题。枚举数为8!= 40320个,枚举数不会超过它。
又因为位置不同行不同列不同对角线的限制,一些排列不会把整个排列都列出就可以判断不符合情况(这也是递归构造和直接枚举的不同,递归构造可以把生成和检查的过程有机结合起来,而直接枚举必须生成所有可能的解,然后一一检查),表现在解答树中,就是只有满足条件的结点有分支,不满足条件的没有分支而是回溯了。
同行同列的判断条件好想,同对角线的条件是:行数差的绝对值等于列数差的绝对值。判断时用当前的行、列数和已经排好的行、列数比较。(祖父结点是排好的/A[0]~A[cur - 1]是排好的)
#include<iostream> using namespace std; void f(int A[], int cur, int n) { if(cur == 8) { for(int i = 0; i < 8 ; i ++) { cout << A[i] << ','; } cout << endl; return ; } for(int i = 0; i < n; i++) { bool flag = true; for(int j = 0; j < cur; j ++) { if(i == A[j]) { flag = false; break; } if(cur - j == i - A[j] || cur - j == A[j] - i) { flag = false; break; } } if(flag) { A[cur] = i; f(A, cur + 1, n); } } } int main() { int A[8]; f(A, 0, 8); return 0; }