在刘汝佳老师的书中对于8皇后问题的分析(我感觉非常经典):
8皇后问题可行的解:92个
回溯的定义: 当把问题分解成若干的步骤并递归的求解时候,如果当前步骤没有合法的选择,则函数将返回上一级递归调用,这种现象称为回溯。这是这个原因,递归枚举算法常被称为回溯法,应用十分普遍。
一共有3种思考的出发点:
- 从64个格子中选一个子集,使得"子集中恰好有8个格子,且任意两个选出的格子都不在同一行,同一列或同一对角线上",这正是子集的枚举问题。然而,64个格子的子集有264个,太大了,这并不是一个很好的模型
- 从64个格子中选8个格子,这是组合生成问题。根据组合数学,有c864 种方案,但是任然是不够好的
- 经过思考,不难发现以下的事实:恰好每行每列各放置一个皇后 。如果使用c[x]表示第x行皇后的列编号,则问题就变成了全排列问题。而0-7的全排列共有8!个,枚举的量是不会超过它。
上代码:
#include<iostream>
#include<math.h>
using namespace std;
int a[8][8]; //相当于放置八个皇后的棋盘
int dfs(int n); //深度优先搜索
bool isOk(int m,int n); //判断(m,n)位置上是否可以放置一个皇后
int count = 0; //用于记录总的可行的解的数量
int main(){
dfs(0);
cout << count;
}
int dfs(int n){ //第几层的皇后
if (n > 7){
for (int i=0; i<8; i++){
for (int j=0; j<8; j++)
cout << " " << a[i][j];
cout << endl;
}
count++;
cout << endl;
return 0;
}
for (int i=0; i<8; i++){
if (isOk(n,i)){ //判断该位置是否可以放置
a[n][i] = (n+1);
dfs(++n);
--n; //考虑该行棋盘的其他位置的时候,将环境还原
a[n][i] = 0;
}
}
}
bool isOk(int m,int n){
if (a[m][n] == 0){
for (int i=0; i<8; i++){
for (int j=0; j<8; j++){
if (a[i][j] != 0){
if (abs(m-i) == abs(n-j)|| i==m || j==n)
return false;
}
}
}
}else{
return false;
}
return true;
}
8皇后问题可行的解:92个
回溯的定义: 当把问题分解成若干的步骤并递归的求解时候,如果当前步骤没有合法的选择,则函数将返回上一级递归调用,这种现象称为回溯。这是这个原因,递归枚举算法常被称为回溯法,应用十分普遍。
下面是书中的代码:
书中的代码使用的是一位数据就解决的问题:
#include<iostream>
//使用一维数组解决问题
using namespace std;
int c[8];
int n=8; //代表的是8个皇后,同时是8维的棋盘
void search(int cur);
int tot = 0;
int main(){
search(0);
cout <<tot;
}
void search(int cur){//cur标志的是现在正在处理的是第几个皇后
if (cur == n){
tot ++; //tot代表的是可行解的总数量
}else{
for (int i=0; i<n; i++){ //对于每个皇后都要考虑一行的8个位置
int ok = 1;
c[cur] = i;
for (int j=0; j<cur; j++) //检查和前面的皇后是否冲突
if (c[cur]==c[j] || cur-c[cur]==j-c[j] || cur+c[cur]==j+c[j]){
ok = 0;
break;
}
if(ok)
search(cur+1);
}
}
}