在做这道题之前搜了一下回溯和递归的区别,递归就是一个劲的往下搜,搜到头了走不通了,再倒回来换条路继续搜,而回溯就是搜到结果以后再回头重新换个点继续重新搜。
这道题是紫书上P191的一道例题,也是一道经典的回溯搜索题,题的描述就是有一个8*8的棋盘,然后有8枚棋子,问一行或者一排或者对角线上只能放一枚棋子,能有多少种放法。
思路就是按列去搜索,对于选定的列,然后再依次对行进行遍历,判断这个点是否满足条件,满足的话就选定该点进行下一列的搜索。对于判断是否满足条件的话,这里我用pre[x]=y,表示y行x列,所以当|pre[x] - pre[y]| == |x - y|的时候,肯定是在一条对角线上的。判断是否在同一行或者同一列就很好判断了,剩下的就看代码吧。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
const int MAXN = 10;
int pre[MAXN];
int Queen_Num = 8;
int num = 0;
void Output(int pre[]){
for(int i = 0; i < Queen_Num; i++){
printf("(%d, %d)\t",pre[i] + 1, i + 1); // 输出坐标
}
printf("\n");
num++; // 方案数++
}
bool check(int pre[], int col){
for(int i = 0; i < col; i++){ // 因为时从第一列开始放,所以只需判断之前的列的行是否重复
if(pre[i] == pre[col] || abs(pre[i] - pre[col]) == col - i){ // 当在同一对角线上时,无论主副都有|pre[i]-pre[col]|==|i-col|
return false;
}
}
return true;
}
void dfs(int pre[], int col){
if(col == Queen_Num){ // 当列数达到8时调用输出函数
Output(pre);
return ;
}
for(int i = 0; i < Queen_Num; i++){ // col为列,遍历行
pre[col] = i; // 将第i行col列存入数组
if(check(pre,col)){ // 判断这样放是否可行
dfs(pre,col+1); // 进行下一列
}
}
}
int main()
{
memset(pre,0,sizeof(pre));
dfs(pre,0); // 从第0列开始
printf("总方案数:%d\n",num);
return 0;
}