当已经放置了一部分皇后以后,对应生成了排列的一部分,可能剩余的皇后无论怎样放置都是不合法的。例如图B中https://blog.csdn.net/qq_36666756/article/details/82958653。当已经放置三个皇后后(对应生成了一部分排列,即351),可以发现剩下的两个皇后无论怎么放置都会产生冲突,就不用继续往下递归了。
#include<cstdio>
#include<stdlib.h>
const int maxn=10;
int n,P[maxn],hashTable[maxn]={false};
int count=0;
/*就是按照一行一行的递归,把每一行分成一个子问题*/
void generateP(int index) //index表示的是列号
{
if(index==n+1)//递归边界,生成一个合法的方案
{
count++;//能到达这里的一定是合法的
return;
}
for(int x=1;x<=n;x++){ //第x行
if(hashTable[x]==false)//第x行还没有皇后
{
bool flag=true;//true表示当前皇后和之前的皇后不会冲突
for(int pre=1;pre<index;pre++)//遍历之前的皇后,为什么是pre<index是因为在一个子问题时index在不断增加,只要前面的皇后的位置确定了,后面的
//就不用遍历了
{
//第index列的皇后的行号为x,第pre列皇后的行号为p[pre]
if(abs(x-P[pre])==abs(index-pre))
{
flag=false;
break;
}
}
if(flag)//如果可以吧皇后放在第x行
{
P[index]=x;//令第index列的皇后的行号为x
hashTable[x]=true;//第x行被占用
generateP(index+1);//处理下一行皇后,index+1表示下一行是因为当上一个子问题未达边界index表示的是列,到达边界后,index
//的含义就不是列的含义了,表示的是下一个子问题了表示的是下一行了,应为最开始传进来的是表示第几个也就是第几行。
hashTable[x]=false;//表示其中一个子问题递归完
}
}
}
}
int main()
{
n=8;
generateP(1);
printf("%d",count);
return 0;
}
暴力法与递归回溯法的区别:
暴力法是要计算完全部的排列才计算其中的排列是否满足n皇后的条件,也就是要在边界条件里面来判断是否满足条件。暴力法中的边界条件中的第二个循环j=i+1是应为两个皇后的位置不能相同,相同后就表示在一个位置上了,一个位置上就相当于少了一个位置。
回溯法中是将每一行看做一个子问题,通过不断迭代求得这些子问题的解是否满足条件。也就是从1-n行中,先计算第一行,然后不断计算增加的列,如果在增加的列的过程中,如果遇到不满足条件的情况就退出,迭代下一个满足条件的排列。其中index在generateP()中表示下一行也就是下一个子问题,在for循环中是表示下一列,这种列是变化的,应为index是在递归中的过程中需要通过执行index+1来执行增加来达到边界,同时,index也为了当前面满足皇后的位置放置,但是在index后面几个不满足条件的情况下就停止循环,表示不满足位置放置的条件。这些条件使index被混用了。