回溯基本思想
-
适用:求解搜索问题和优化问题
-
搜索空间:树,结点对应部分解向量,可行解在树叶上
-
搜索策略:深度优先、广度优先、函数优先、深度广度结合等
-
若满足约束条件,则分支扩张解向量,若不满足约束条件,则回溯到该结点的父结点
对于四皇后问题,搜索空间是一颗四叉树。树有5层,第一层是原始问题,第二层有4个结点,分别表示第一个皇后放在第一行第1,2,3,4列的情况下问题的解,第三层有16个结点,分别表示在第一、二行放置皇后以后问题的解。
常规方法判断棋盘位置时需要判断所在列以及四个斜对角是否已经放有皇后,有点麻烦。
为了简化问题,我们可以设置数组col[n]、mainDiag[2*n-1]和subDiag[2*n-1]分别记录棋盘的各个列、各条主对角线和次对角线是否已经有皇后。
比如棋盘board[i][j]的位置放了皇后,我们需要将col[j], mainDiag[j - i + size - 1]和subDiag[i + j]置为1。
在棋盘类中,使用了指针用来动态分配内存。
ChessBoard.h的内容:
#pragma once
#include <iostream>
using namespace std;
class ChessBoard
{
public:
ChessBoard(int n = 4);
~ChessBoard();
void Queen(int i = 0);
void print(int count);
private:
int size; //棋盘大小
int** board; //棋盘数组,board[i][j] = 0表示该位置没有皇后,1表示该位置有皇后
int* col; //col[i] = 0表示第i列没有皇后,为1表示第i列有皇后
int* mainDiag; //mainDiag[i] = 0表示主对角线i没有皇后,为1表示有皇后
int* subDiag; //subDiag[i] = 0表示副对角线i没有皇后,为1表示有皇后
};
ChessBoard.cpp的内容:
#include "ChessBoard.h"
ChessBoard::ChessBoard(int n)
{
size = n;
// 将为成员指针开辟内存并且将指针对应的值全部初始化为0
board = new int*[n];
for (int i = 0; i < n; i++) {
board[i] = new int[n];
memset(board[i], 0, n * sizeof(int));
}
col = new int[n];
memset(col, 0, n * sizeof(int));
mainDiag = new int[2 * n - 1];
memset(mainDiag, 0, (2 * n - 1 )* sizeof(int));
subDiag = new int[2 * n - 1];
memset(subDiag, 0, (2 * n - 1) * sizeof(int));
}
ChessBoard::~ChessBoard()
{
for (int i = 0; i < size; i++) {
delete[] board[i];
}
delete board; //这个记得要删除
delete[] col;
delete[] mainDiag;
delete[] subDiag;
}
// 逐行进行递归
void ChessBoard::Queen(int i)
{
static int count = 0;
for (int j = 0; j < size; j++) { //j是列号
if (!col[j] && !mainDiag[j - i + size - 1] && !subDiag[i + j]) { // 如果满足约束条件,则继续判断当前结点的深度
board[i][j] = 1; //放棋子
if (i == size - 1) { //当结点扩展到第size层时便可以得到一个解
count++;
print(count);
//return; 这里不能着急return,因为还没有回溯
}
else { //继续扩展结点
col[j] = mainDiag[j - i + size - 1] = subDiag[i + j] = 1; //相应的列、对角线记录好
Queen(i + 1); //dfs,向下扩展
}
//以下两行代码是回溯,要写if里面,第i + 1层函数执行完以后就会回到i层的for循环里面
board[i][j] = 0;
col[j] = mainDiag[j - i + size - 1] = subDiag[i + j] = 0;
}
}
}
// 打印当前棋盘的状态
void ChessBoard::print(int count)
{
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
if (board[i][j] == 0)
printf("·");
else
printf("o");
}
printf("\n");
}
printf("上面是第%d种可能的情况\n", count);
}
主函数:
#include <iostream>
#include <vector>
#include "ChessBoard.h"
using namespace std;
int main()
{
ChessBoard chessBoard(8);
chessBoard.Queen();
return 0;
}
当然,如果想测试其他的问题,比如4皇后,将构造函数相应修改即可。
最终结果: