递归枚举排列法
//生成排列递归法源代码、思路来自:《算法竞赛入门经典》第二版的 P185 、 P192
//八皇后问题初试
//根据8个皇后在不同行不同列
//步骤:填第一行的皇后、填第二行的皇后(不能与之前的同列)、...、填第八行的皇后
//相当于 枚举排列:01234567表示 每行皇后(0,1,2,...,7)所在的列数
//排列出一个后,判断是否处于对角线的
//2020/3/31
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
const int k = 8; //k皇后求解
int g_count = 0;
void Permu(int n,int *A,int cur) {
if (cur == n) {
// for (int i = 0; i < k; i++) {
// cout << A[i];
// }
// cout << endl;
//判断是八皇后否在一个对角线上
bool is_true = true;
for (int i = 0; i < n; i++) {
for (int j = i+1 ; j < n; j++) {
int r = fabs(i-j); //行列的差值若相同,则处于同一对角线上
int c = fabs(A[i] - A[j]);
if (r * c > 0 && r == c) {
is_true = false;
}
}
}
if (is_true) {
g_count++;
for (int i = 0; i < k; i++) {
for (int j = 0; j<k; j++) {
if (j == A[i]) {
cout << "Q";
}
else
cout << "*";
}
cout << endl;
}
cout << "______________分割线\n";
}
}
else {
for (int i = 0; i < k; i++) { //往A[i]中尝试 0-7 的各种数
int is_ok = true;
for (int j = 0; j < cur; j++) {
if (i == A[j]) is_ok = false;
}
if (is_ok) {
A[cur] = i;
Permu(k,A,cur+1);
}
}
}
}
int main() {
int A[k];
Permu(k,A,0);
cout << "Total number = " << g_count << endl;
return 0;
}
在递归函数中加入判断条件后(咱也不知道这算不算回溯法)
根据紫书所说,回溯法就是在递归的同时也进行判断,不符合K皇后条件的枚举不展开结点了,直接回到上一步调用。
例如:递归到 排列 01,此时其实已经不符合条件了(处于同一对角线),没必要继续展开了,直接返回上一步,这样可以省下一大笔功夫
这就是回溯法,在递归的基础上进行优化的算法。
我比较了一下两种算法的运行时间,哇,递归法在K = 11时候已经等半天都出不来结果(运行了37.92s),回溯法秒出结果(0.17s)。
这优化的可不是一点点呐,算法真的太强了!哇!
/*
增加递归判断,实现回溯法
*/
//八皇后问题初试
//根据8个皇后在不同行不同列
//步骤:填第一行的皇后、填第二行的皇后(不能与之前的同列)、...、填第八行的皇后
//相当于 枚举排列:12345678表示第i(1-8)行皇后在第i(式子里分别代入)列
//排列出一个后,判断是否处于对角线的
//2020/3/31
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
const int k = 11; //k皇后求解
int g_count = 0;
void Permu(int n,int *A,int cur) {
if (cur == n) {
// for (int i = 0; i < k; i++) {
// cout << A[i];
// }
// cout << endl;
//判断是八皇后否在一个对角线上
bool is_true = true;
for (int i = 0; i < n; i++) {
for (int j = i+1 ; j < n; j++) {
int r = fabs(i-j); //行列的差值若相同,则处于同一对角线上
int c = fabs(A[i] - A[j]);
if (r * c > 0 && r == c) {
is_true = false;
}
}
}
if (is_true) {
g_count++;
// for (int i = 0; i < k; i++) {
// for (int j = 0; j<k; j++) {
// if (j == A[i]) {
// cout << "Q";
// }
// else
// cout << "*";
// }
// cout << endl;
// }
// cout << "______________分割线\n";
}
}
else {
bool is_legal = true; //判断是否继续递归的代码段,若不满足皇后条件,则回溯
for (int i = 0; i < cur; i++) {
for (int j = i+1 ; j < cur; j++) {
int r = fabs(i-j); //行列的差值若相同,则处于同一对角线上
int c = fabs(A[i] - A[j]);
if (r * c > 0 && r == c) {
is_legal = false;
}
}
}
if (!is_legal) return; //直接回溯,不要递归下去了
for (int i = 0; i < k; i++) { //往A[i]中尝试 0-7 的各种数
int is_ok = true;
for (int j = 0; j < cur; j++) {
if (i == A[j]) is_ok = false;
}
if (is_ok) {
A[cur] = i;
Permu(k,A,cur+1);
}
}
}
}
int main() {
int A[k];
Permu(k,A,0);
cout << "Total number = " << g_count << endl;
return 0;
}