N皇后问题
对于N皇后问题,这里以4皇后问题为例给出问题定义。4皇后问题是指在4X4的国际象棋盘上,放置4个“皇后”棋子,同时满足这样的条件:任意两个皇后不出现在同一行、同一列、同一正斜线和同一逆斜线上。
这里以4皇后问题为例,采用回朔法的思想进行4皇后的放置,过程见下图。假设第1个皇后放在第1行1列(图1),则第2个皇后可放置的第一个候选位置是第2行第3列(图2)。这样第3个皇后在第3行就没有候选位置(图3),需要“回退”到第2行,重新摆放第2个皇后,改为第2行第4列(图4),则第3个皇后可放置在第3行第2列(图5)。这时,第4个皇后在第4行也无法放置(图6),需“回退”重新摆放第3个皇后,同样也无可选位置(图7)。继续回退重新摆放第2个皇后,无可选位置(图8),因此,重新回退到第1行。将第1个皇后摆放在第1行第2列(图9)。然后依次摆放其他皇后,找到第1个合适的解(图12)。
首先设计用来表示皇后的数据结构,采用顺序栈的方式存储皇后为止。
//用来表示皇后的数据结构
typedef struct
{
int col; //列坐标值
int row; //行坐标值
}Point; //数据元素为皇后的位置坐标
typedef Point ElemType;
然后,考虑是否存在皇后冲突判别方法。由每行只能放一个皇后,所以只要判断新放置的皇后与前面已放置的皇后是否在同一列、同一正斜线和同一逆斜线对于判断是否在同一列,就看新放置的皇后列坐标是否与栈中其他皇后列坐标相等。对于同一正斜线、逆斜线的两个点,其斜率为1或 -1,即|y2 - y1| = |x2 - x1|。判断算法如下:
Status JudgeQueenConfliction(Point newQueen, SeqStack StkQueen) //皇后冲突判断算法
{
//判断新皇后位置和已有皇后是否有冲突,没有冲突返回OK,有冲突返回ERROR
ElemType* pCurQueen;
int x1 = newQueen.col;
int y1 = newQueen.row;
pCurQueen = StkQueen.pBase;
int tag = OK;
while (pCurQueen < StkQueen.pTop)
{
int x2 = pCurQueen->col;
int y2 = pCurQueen->row;
if (x1 == x2) //如果新的皇后和前面的皇后在同一列
{
tag = ERROR;
break;
}
if (abs(x2 - x1) == abs(y2 - y1)) //新皇后和前面的皇后在同一正斜线或逆斜线
{
tag = ERROR;
break;
}
pCurQueen++;
}
return tag;
}
最后,给出4皇后问题的算法思想:
(1)初始状态:将第1个皇后放置在第1行第1列,并压栈;当前处理行编号为2,列编号为1.
(2)执行过程:
当前处理的行编号不大于且列编号不大于4:
从该行当前编号开始,检查是否有冲突;
若找不到冲突位置,则:
放置皇后,将位置压栈;当前处理行编号加1,列编号置为1;
当前行编号大于4时:
本轮全部放置完成,输出4个皇后的位置。弹栈,修改最后一个皇后位置,进行下一种
解试探。
否则:
将栈顶皇后弹栈,当前处理行改为该皇后所在的行,当前列编号置为该皇后所在列的后
一列,若列发生越界,则继续执行弹栈处理知道找到合适位置。
N皇后放置非递归算法
Status PlaceQueen(int N) //N皇后放置非递归算法
{
SeqStack StkQueen;
Point curQueen;
InitStack(StkQueen);
int resultCount = 0; //记录满足要求的解数量
curQueen.col = 1;
curQueen.row = 1;
Push(StkQueen, curQueen);
curQueen.col = 2;
curQueen.row = 1;
int ret;
while (curQueen.row <= N && curQueen.col <= N)
{
while (curQueen.col <= N)
{
ret = JudgeQueenConfliction(curQueen, StkQueen);
if (ret == OK) break; //这一行的该位置为合适位置
curQueen.col = curQueen.col + 1; //存在冲突,则列往后移动一个位置
}
if (ret == OK) //如果在该行上存在合适的位置
{
Push(StkQueen, curQueen);
curQueen.row = curQueen.row + 1;
curQueen.col = 1;
if (curQueen.row > N) //已经将最后一行放置完成,即已经找到了一个解
{
OutputResult(StkQueen, N); //根据保存的栈,输出满足条件的N皇后放置效果
resultCount++; //解的数量加1
Pop(StkQueen, curQueen); //弹栈
curQueen.col = curQueen.col + 1;
while (curQueen.col > N && !StackEmpty(StkQueen)) //如果回退不成功则继续回退
{
Pop(StkQueen, curQueen);
curQueen.col = curQueen.col + 1;
}
}
}
else //在本行上没有找到合适的位置,执行回退
{
Pop(StkQueen, curQueen);
curQueen.col = curQueen.col + 1;
while (curQueen.col > N && !StackEmpty(StkQueen)) //如果回退不成功,则继续回退
{
Pop(StkQueen, curQueen);
curQueen.col = curQueen.col + 1;
}
}
}
printf("解法个数:%d", resultCount);
DestroyStack(StkQueen);
return OK;
}
输出N皇后放置位置的函数如下:这里只设置了最大为8皇后,可以自己设置最大值
Status OutputResult(SeqStack StkQueen, int N) //输出N皇后放置的一种解法
{
int Nqueen[8][8] = {0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 };//放置皇后的数组初始化
ElemType* p = StkQueen.pBase;
while (p < StkQueen.pTop)
{
Nqueen[p->row - 1][p->col - 1] = 1;
p++;
}
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
printf("%d ", Nqueen[i][j]);
}
printf("\n");
}
printf("\n\n");
return OK;
}
完整代码如下
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#define STACKINITSIZE 256 //初次分配空间大小
#define STACKINCREMENT 128 //空间分配增量大小
#define OK 1
#define ERROR 0
typedef int Status;
//用来表示皇后的数据结构
typedef struct
{
int col; //列坐标值
int row; //行坐标值
}Point; //数据元素为皇后的位置坐标
typedef Point ElemType;
typedef struct SeqStack
{
ElemType* pBase; //动态存储空间的基地址,作为栈底
ElemType* pTop; //栈顶指针,指向真实栈顶元素的下一位置
int stacksize; //当前已分配的存储空间大小
}SeqStack;
Status InitStack(SeqStack& S) //顺序栈初始化
{
S.pBase = (ElemType*)malloc(sizeof(ElemType));
if (S.pBase == NULL) return ERROR;
S.pTop = S.pBase;
S.stacksize = STACKINITSIZE;
return OK;
}
Status DestroyStack(SeqStack& S) //顺序栈销毁
{
if (S.pBase != NULL)
{
free(S.pBase);
S.pBase = NULL;
}
S.pTop = NULL;
S.stacksize = 0;
return OK;
}
Status ClearStack(SeqStack& S) //顺序栈清空
{
S.pTop = S.pBase;
return OK;
}
Status StackEmpty(SeqStack S) //顺序栈判空
{
if (S.pTop != S.pBase) return ERROR;
return OK;
}
Status StackLength(SeqStack S) //顺序栈元素个数
{
ElemType* p = S.pBase;
int i = 0;
while (p != S.pTop)
{
p = p + 1;
i++;
}
return i;
}
Status GetTop(SeqStack S, ElemType& e) //顺序栈栈顶元素
{
if (S.pTop == S.pBase) return ERROR;
e = *(S.pTop - 1);
return OK;
}
Status StackTraverse(SeqStack S) //顺序栈遍历
{
if (S.pBase == S.pTop) return ERROR;
ElemType* p = S.pBase;
while (p != S.pTop)
{
printf("%d ", *p);
p = p + 1;
}
printf("\n");
return OK;
}
Status Push(SeqStack& S, ElemType e) //顺序栈进栈
{
if (S.pTop - S.pBase >= S.stacksize)
{
S.pBase = (ElemType*)realloc(S.pBase, sizeof(ElemType) * (S.stacksize + STACKINCREMENT));
if (S.pBase == NULL) exit(0);
S.pTop = S.pBase + S.stacksize;
S.stacksize = S.stacksize + STACKINCREMENT;
}
*S.pTop = e;
S.pTop++;
return OK;
}
Status Pop(SeqStack& S, ElemType& e) //顺序栈出栈
{
if (S.pBase == S.pTop) return ERROR;
e = *(S.pTop - 1);
S.pTop--;
return OK;
}
Status JudgeQueenConfliction(Point newQueen, SeqStack StkQueen) //皇后冲突判断算法
{
//判断新皇后位置和已有皇后是否有冲突,没有冲突返回OK,有冲突返回ERROR
ElemType* pCurQueen;
int x1 = newQueen.col;
int y1 = newQueen.row;
pCurQueen = StkQueen.pBase;
int tag = OK;
while (pCurQueen < StkQueen.pTop)
{
int x2 = pCurQueen->col;
int y2 = pCurQueen->row;
if (x1 == x2) //如果新的皇后和前面的皇后在同一列
{
tag = ERROR;
break;
}
if (abs(x2 - x1) == abs(y2 - y1)) //新皇后和前面的皇后在同一正斜线或逆斜线
{
tag = ERROR;
break;
}
pCurQueen++;
}
return tag;
}
Status OutputResult(SeqStack StkQueen, int N) //输出N皇后放置的一种解法
{
int Nqueen[8][8] = {0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0 };//放置皇后的数组初始化
ElemType* p = StkQueen.pBase;
while (p < StkQueen.pTop)
{
Nqueen[p->row - 1][p->col - 1] = 1;
p++;
}
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
printf("%d ", Nqueen[i][j]);
}
printf("\n");
}
printf("\n\n");
return OK;
}
Status PlaceQueen(int N) //N皇后放置非递归算法
{
SeqStack StkQueen;
Point curQueen;
InitStack(StkQueen);
int resultCount = 0; //记录满足要求的解数量
curQueen.col = 1;
curQueen.row = 1;
Push(StkQueen, curQueen);
curQueen.col = 2;
curQueen.row = 1;
int ret;
while (curQueen.row <= N && curQueen.col <= N)
{
while (curQueen.col <= N)
{
ret = JudgeQueenConfliction(curQueen, StkQueen);
if (ret == OK) break; //这一行的该位置为合适位置
curQueen.col = curQueen.col + 1; //存在冲突,则列往后移动一个位置
}
if (ret == OK) //如果在该行上存在合适的位置
{
Push(StkQueen, curQueen);
curQueen.row = curQueen.row + 1;
curQueen.col = 1;
if (curQueen.row > N) //已经将最后一行放置完成,即已经找到了一个解
{
OutputResult(StkQueen, N); //根据保存的栈,输出满足条件的N皇后放置效果
resultCount++; //解的数量加1
Pop(StkQueen, curQueen); //弹栈
curQueen.col = curQueen.col + 1;
while (curQueen.col > N && !StackEmpty(StkQueen)) //如果回退不成功则继续回退
{
Pop(StkQueen, curQueen);
curQueen.col = curQueen.col + 1;
}
}
}
else //在本行上没有找到合适的位置,执行回退
{
Pop(StkQueen, curQueen);
curQueen.col = curQueen.col + 1;
while (curQueen.col > N && !StackEmpty(StkQueen)) //如果回退不成功,则继续回退
{
Pop(StkQueen, curQueen);
curQueen.col = curQueen.col + 1;
}
}
}
printf("解法个数:%d", resultCount);
DestroyStack(StkQueen);
return OK;
}
int main()
{
int N;
printf("N皇后问题----输入N(N <= 8):");
scanf_s("%d", &N);
printf("==========解法如下==========\n");
PlaceQueen(N);
return 0;
}
运行结果如下: