递归法
递归应该按照分析出“大问题”、“小问题”,写出递归模型,写出递归算法的步骤进行考虑。
- 递归函数为
queen(i, n)
,表示“在i ~ n行的某一列放置剩下的n-i+1个皇后”(即,在n~1-i行上已经放好了i-1个皇后); queen(i, n)
是“大问题”,queen(i+1, n)
是“小问题”。- 这里用伪代码表示递归模型:
if (i > n)
n个皇后放置完毕,输出一个解;
else
{
在第i行找到一个合适的位置(i, j),放置一个皇后;
queen(i+1, n);
}
- 完整代码
#include<iostream>
#include<cmath>
#include<cstring>
#define maxn 10
int q[maxn+1]; //存放各皇后所在的列号,即(i, q[i])为一个皇后的位置
//为了简便,不使用索引为0的行/列位置
int count = 0; //累计可行解的个数
using namespace std;
bool place(int i, int j) //测试(i, j)位置能否摆放皇后
{
if (i == 1) //第一个皇后可以放置到每一列,所以直接返回true
return true;
for (int k = 1; k < i; k++)//与已放置了皇后的行进行比较、判断
if (q[k] == j || (abs(q[k] - j) == abs(k - i)))
return false;
return true;
}
void queen(int i, int n)
{
if (i > n)
{
count += 1;
for (i = 1; i <= n; i++)
printf("(%d, %d)", i, q[i]);
cout << endl;
}
else
for (int j = 1; j <= n; j++) //在第i行上试探每一个列j
if (place(i, j))
{
q[i] = j;
queen(i+1, n);
}
}
int main()
{
int n;
cin >> n;
memset(q, 0, sizeof(q));
queen(1, n);//放置1 ~ n个皇后
cout << "可行解个数:" << count;
return 0;
}
回溯法
- 回溯算法是有框架的,应该深刻理解并记住框架,然后灵活套用。
- 本题用到的解题框架是:递归回溯框架之解空间为子集树的情况。(请自行想象出本题的子集树…)
#include<iostream>
#include<cmath>
#include<cstring>
#define maxn 10
using namespace std;
int n;
int q[maxn+1]; //存放各皇后所在的列号,即(i, q[i])为一个皇后的位置
//为了简便,不使用索引为0的行/列位置
int count = 0; //累计可行解的个数
bool place(int i) //判断第i行的q[i]列能否摆放皇后
{
if (i == 1) //第一个皇后可以放置到每一列,所以直接返回true
return true;
for (int k = 1; k < i; k++) //与已放置了皇后的行进行比较、判断
if (q[k] == q[i] || (abs(q[k] - q[i]) == abs(k - i)))
return false;
return true;
}
void backtrack(int i)
{
if (i > n) //搜索到叶子结点(根结点为第一层时,叶子结点为第n+1层,所以此处判断条件为i > n),输出一个可行解
{
count += 1;
for (int x = 1; x <= n; x++)
printf("(%d, %d) ", x, q[x]);
cout << endl;
}
else
{
for (int j = 1; j <= n; j++)//用j枚举i所有可能的路径
{
q[i] = j; //产生一个可能的解分量
if(place(i)) //q[i]满足约束条件
backtrack(i+1); //继续下一层
}
}
}
void Queens()
{
backtrack(1); //这里设定根结点为第一层,所以从1开始调用回溯算法backtrack()
}
int main()
{
cin >> n;
Queens();
cout << "可行解个数:" << count;
return 0;
}
- 回溯法利用到了递归以及==深度优先遍历(DFS)==的思想。