N 皇后问题解决


题目:N 皇后问题源自国际象棋,所有棋子中权力最大的称为皇后,它可以直着走、横着走、斜着走(沿 45 度角),可以攻击移动途中遇到的任何棋子。N 皇后问题的具体内容是:如何将 N 个皇后摆放在 N*N 的棋盘中,使它们无法相互攻击。

一、问题分析

该问题要求把n个皇后放在一个n×n的棋盘上,使任何两个皇后不能同行不能同列也不能位于同一对角线上。用回溯的思想对它求解:因为每个皇后都必须占据一行,所以我们的问题转化为给每行的皇后分配一个合适的列。
n=1,2,3时问题很简单,所以我们以n=4开始考虑,求解过程如下:
在这里插入图片描述
我们从空棋盘开始,首先将皇后1放到它所在行的第一个可能位置(此时为第一列),即皇后1的坐标为(1,1);对于皇后2,尝试将其填入第一列检查是否符合规则,发现失败,第二列也失败,第三列符合要求,即将皇后2填入(2,3);再将皇后3依规则找第一个可能的位置,但是发现四个位置都不符合,所以将算法回溯,把皇后2放在下一个可能位置,即(2,4)上,这样皇后3可以放在(3,2),去找皇后4能不能放,此时又发现没有符合的位置,然后回溯到皇后3,皇后3没有再可以尝试的位置,回溯到皇后2也没有再可以尝试的位置,回溯到皇后1,把皇后1放在(1,2),接着按相同步骤将皇后2放在(2,4),皇后3放在(3,1),皇后4放在(4,3),这就是n=4时的一个解,接着再次回溯找剩下的解。

二、算法思路

1、本题一共使用两个算法来进行解决,第一个是暴力搜索法,即将皇后所有可能的排列方式都排列出来并进行判断,算法中主要使用了一个数组和一个可以用来判断行列还有斜线上检测是否有棋子存在的判断方法来进行判断如果符合题目要求就保存下来,最后穷举完成后输出所有可能的排序。
2、第二种算法是回溯法。算法思路是从[0][0]号位置开始,如果在某一行找到了一个可以放置皇后的位置,记录该行的位置,并且更新相应的三个位置数组后进入下一行进行判断。如果在某行没有找到位置,那么意味着前面发生了错误,需要返回上一行进入该行的位置,将保存的上一行皇后的位置从三个位置数组中删除,接着判断上一行下一个未判断的空位。如果在最后一行找到了位置,那么意味着找到了一个解,接着保存该解。并且删除三个位置数组的位置,回溯至上一级,返回上一行对未判断的位置继续进行求解。直至对第一行所有位置扫描一遍后,找到所有解为止。

三、代码

1、暴力求解

#include<stdio.h>
#include<math.h>
#define n 5 
int place(int a[n])
{
	int i, j;
	for(i = 0; i < n; i++)
		for (j = i + 1; j < n; j++)
		{
			if ((a[i] == a[j]) || (abs(a[i] - a[j]) == abs(i - j)))//不同列和斜线
				return 0;
		}
	return 1;
}
int main()
{
	int a[n];
	int num, temp;
	int i;
	int count = 0;

	for (num = 0; num < pow(n, n); num++)
	{
		temp = num;
		for (i = 0; i < n; i++)
		{
			a[i] = temp%n;
			temp = temp / n;
		}
			if (place(a))
			{
				count++;
				for (i = 0; i < n; i++)
				{
					printf("(%d,%d) ", i+1,a[i]+1);
				}
				putchar('\n');
			}
		
	}
	printf("total is : %d\n", count);
	return 0;
}

运行结果如下:
在这里插入图片描述
2、回溯法

def NQueens(n):
    def checkBoard(rowIndex):
        '''
        # 检查盘面是否成立
        :param rowIndex:
        :return:
        '''
        for i in range(rowIndex):          # rowIdex表示当前行号
            if cols[i] == cols[rowIndex]:  # 检查竖线(即列)
                return False
            if abs(cols[i] - cols[rowIndex]) == rowIndex - i:   # 检查斜线
                return False
        return True

    def helper(rowIndex):
        if rowIndex == n:       # 递归结束的边界条件
            board = [[0 for _ in range(n)] for _ in range(n)]
            for i in range(n):
                board[i][cols[i]] = 1
            res.append(board)
            return
        for i in range(n):      # 依次尝试当前行的空格位置()
            cols[rowIndex] = i
            if checkBoard(rowIndex):
                helper(rowIndex + 1)     # 递归调用给子函数

    cols = [False for _ in range(n)]
    res = []          # 定义每行皇后的纵坐标
    helper(0)
    return res        # 结束NQueens函数


if __name__ == '__main__':
    li = NQueens(5)         # 函数中的值,表示解决的8皇后问题
    for i in range(len(li)):
        print(f'{i+1}:{li[i]}')

运行结果:
在这里插入图片描述

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
N皇后问题和子集和数问题都是经典的组合优化问题,两者的解决步骤有一些相似之处,但也有不同之处。 解决N皇后问题的步骤如下: 1. 定义问题:在N×N的棋盘上摆放N个皇后,使得它们互相之间不能攻击到对方。 2. 设计算法:采用回溯算法,从棋盘的第一列开始,逐列地尝试放置皇后,当发现某个位置无法放置时,回溯到上一列并尝试下一个位置,直到所有的皇后都放置完毕或无解。 3. 实现算法:在算法实现中,需要设计一些辅助函数,如判断某个位置是否安全、打印输出当前的解等。 4. 测试算法:通过对不同规模的问题进行测试,验证算法的正确性和效率。 解决子集和数问题的步骤如下: 1. 定义问题:给定一个正整数数组和一个目标值,判断是否存在一个子集,使得子集中的元素之和等于目标值。 2. 设计算法:采用回溯算法,从数组的第一个元素开始,逐个地将其加入或不加入子集中,当子集元素之和等于目标值时,记录当前的解并回溯到上一步,直到所有的元素都考虑完毕或无解。 3. 实现算法:在算法实现中,需要设计一些辅助函数,如计算子集元素之和、记录当前的解等。 4. 测试算法:通过对不同规模的问题进行测试,验证算法的正确性和效率。 总的来说,两个问题的解决步骤都包括定义问题、设计算法、实现算法和测试算法,但具体实现时涉及的算法和辅助函数有所不同。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值