N皇后问题

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;
}

运行结果如下:

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值