回溯法与剪枝
同时一点说,就是运用DFS或者BFS一个个找,如果遇到了一种情况,继续找下去会没有意义了,我们就停止寻找,返回上一个状态,举一个简单的例子来说明,比如我们寻找1000以内的素数,如果发现x可以被y整除,那么就没有必要继续找下去了停止搜索,这一步也俗称剪枝,返回x-1,下一个是x+1,这一步大概就是回溯
八皇后问题也算是学习的一个门槛的,
第一步何时终止递归
大概就是 如果我们寻找答案已经到了最后一步,没有找下去的必要了
第二步何时剪枝
对于这道八皇后来说,就是当行与列还有主副对角线都不能走的时候,就剪枝,当然这是白话,剪枝困难的地方是把大白话给抽象成计算机编程语言,拿这道题来说,可以用4个bool数组,分别表示行列主副对角线的状态,行列状态好说,主副对角线的状态可以抽象成 如下
v3[i-L+n] = false;
v4[i+L] = false;
第三步如果还有可能继续找下去
这一步和上一步何时剪枝有时候是可以放在一起的,比如我的代码就是放在一起的
附上完整AC代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN=50;
bool v1[MAXN],v2[MAXN],v3[MAXN],v4[MAXN];
//上面三个分别表示行列主对角线与副对角线是否可以放元素
int a[MAXN][MAXN];
int ans[MAXN]; //第L列棋子所在的行数
int n; //矩阵的列数
int c=0; //拿来计算的
void dfs(int L)
{
if(L==n+1)
{
c++;
//如果当前个数小于3说明要输出
if(c<=3)
{
for (int i = 1; i <= n; ++i)
{
if(i==n)
cout<<ans[i];
else
cout<<ans[i]<<" ";
}
cout<<endl;
}
}
/*
//如果没到终点发现无路可走了剪枝就剪枝
if (!v1[L]&&!v2[ans[L]]&&!v3[ans[L]-L+n]&&!v4[L+ans[L]])
return;
*/
//如果没有回溯到终点了话
v1[L] = false;
//这里其实隐含了剪枝
for (int i = 1; i <= n; ++i)
{
if(v2[i]&&v3[i-L+n]&&v4[i+L])
{
ans[L] = i;
v2[i] = false;
v3[i-L+n] = false;
v4[i+L] = false;
dfs(L+1);
v2[i] = true;
v3[i-L+n] = true;
v4[i+L] = true;
}
}
}
int main(int argc, char const *argv[])
{
cin>>n;
memset(v1,1,sizeof(v1));
memset(v2,1,sizeof(v2));
memset(v3,1,sizeof(v3));
memset(v4,1,sizeof(v4));
dfs(1);
cout<<c;
return 0;
}