序言
在笔试中会遇到一些可以用常用算法就能快速解决的问题,如果对这些算法不熟悉的话,在笔试中是比较吃亏的。
这篇文章学习回溯法及用回溯法解决的八皇后问题。
1. 回溯法
基本思想
有时我们要得到问题的解,先从其中某一种情况进行试探,在试探过程中,一旦发现原来的选择是错误的,那么就退回一步重新选择,然后继续向前试探,反复这样的过程直到求出问题的解。
回溯法在用来求问题的任一解时,只要搜索到问题的一个解就可以结束。这种以深度优先的方式系统地搜索问题的解的算法称为回溯法,它适用于解一些组合数较大的问题。
算法步骤
明确问题的解空间:描述解的形式,定义一个解空间,它包含问题的所有解。
构造约束函数/剪枝函数:约束函数是根据题意给出的,通过描述合法解的一般特征用于去除不合法的解,从而避免继续搜索出这个不合法解的剩余部分。
通过DFS思想完成回溯
2. 八皇后问题
问题描述
- 八皇后问题是在8*8的棋盘上放置8枚皇后,使得棋盘中每个纵向、横向、左上至右下斜向、右上至左下斜向均只有一枚皇后。即各个皇后之间不再同一行、不在同一列也不在对角线。
问题求解
采用回溯法求解。从上至下依次在每一行放置皇后,进行搜索,若在某一行的任意一列放置皇后均不能满足要求,则不再向下搜索,而进行回溯,回溯至有其他列可放置皇后的一行,再向下搜索,直到搜索至最后一行,找到可行解,输出。
步骤
1. 构造n × n的数组。数组下标表示皇后所在行、数组元素表示皇后所在列。 2. 回溯过程:假设前n-1行的皇后已经按照规则排列好,那么可以使用回溯法逐个试出第n行皇后的合法位置。 所有皇后的初始位置都是第0列,那么逐个尝试就是从0试到N-1,如果达到N,仍未找到合法位置, 回退一行,且该行的皇后的位置加1,继续尝试。如果目前处于第0行,还要再回退,说明此问题已再无解。 3. 剪枝函数/约束函数:如果当前行的皇后的位置还是在0到N-1的合法范围内,那么首先要判断该行的皇后是否与前几 行的皇后互相冲突,如果冲突,该行的皇后的位置加1,继续尝试;如果不冲突,判断下一行的皇后。 如果已经是最后一行,说明已经找到一个解,输出这个解。 4. 然后最后一行的皇后的位置加1,继续尝试下一个解。
代码(C,递归方式)
#include <stdio.h>
#include <stdlib.h>
#define bool char
#define true 1
#define false 0
#define N 8
void solve(int row);
bool valid(int row, int column);
void output(void); //输出函数
int matrix[N][N] = {{0}}; //棋盘,声明为全局变量简化valid函数参数
int counter = 0; //方案计数
/* 主函数 */
int main()
{
BackTrack(1);
return 0;
}
/* 递归函数实现回溯算法 */
void BackTrack(int row)
{
int j;
for (j = 0; j < N; j++)
{
matrix[row - 1][j] = 1; //先在第一列放置皇后
if (valid(row - 1, j))
{
if (row == N) output(); //有解,将一直递归至最后一行并输出
else BackTrack(row + 1);
}
matrix[row - 1][j] = 0; //否则,看当前行下一列
}
}
/* 剪枝函数:验证第row行第column列放置皇后是否可行 */
bool valid(int row, int column)
{
if (row == 0) return true; //第一行,无条件输出
//不在同一列
int i = 0, j = 0;
for (; i < row; i++)
{
if (matrix[i][column] == 1)
return false;
}
//不在对角线:主对角线和副对角线
i = row - 1; //当前行是row - 1行,上一行row - 2
j = column - (row - i); //对角线:行差 = 列差,即row - 1 - i = column - j
while (i >= 0 && j >= 0)
{
if (matrix[i][j] == 1)
return false;
i--;
j--;
}
i = row - 1;
j = column + (row - i); //对角线:行差 = 列差,即row - 1 - i = j - column
while (i >= 0 && j <= 7)
{
if (matrix[i][j] == 1)
return false;
i--;
j++;
}
//否则,可行
return true;
}
/* 可行方案输出 */
void output(void)
{
int i, j;
counter++;
printf("solution %dth: \n", counter);
for (i = 0; i < N; i++)
{
for (j = 0; j < N; j++)
{
printf("%d ", matrix[i][j]);
}
printf("\n");
}
printf("\n");
}
Acknowledgements:
http://zephiruswt.blog.51cto.com/5193151/895797
http://blog.csdn.net/daniel_ustc/article/details/17040315
http://blog.csdn.net/sinat_33052719/article/details/51447531
2017.09.12