八皇后问题 C语言实现

 问题:

在8×8格的国际象棋上摆放8个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

 思路:

对于计算机问题的求解,我们完全可以模仿人类的解题思路,并将其抽象化,形成可供计算机执行的代码。

首先,我们考虑使用枚举的方式,但应保证枚举不重复。故我们将八个棋子分别编号,并让编号小的棋子永远在编号大的棋子前,从左到右,从上到下,从编号小的棋子依次排列到编号大的棋子。

按照这个思路,我们可以定义一个8*8的二维数组,二维数组的值代表这个棋子的编号。我们先将第一个棋子放置于0*0的位置,然后距离最近可能的第二个棋子的位置,即1*3,依次类推。这种方法很像一个循环,不过普通的循环显然无法实现这个方案。因此我们参考递归的实现思路,定义一个函数,用来处理这个问题。

当按照这个思路做完,我们会发现没有一个结果符合我们的要求。因此我们回去寻找问题。很容易发现,第七个棋子并不一定放在离第六个棋子最近可能的地方,同理,第二个棋子也不一定放在离第一个棋子最近可能的地方(即位置1*3)。因此,我们要重新对上一个棋子定义位置,直到第一个棋子到达最后的位置,我们才做完了所有可能。

对问题进一步抽象,我们假设对第n个棋子进行操作,在定下第n个棋子的一个位置后再定第n+1个棋子,同时我们还应重新定第n个棋子的位置。至此,我们就能解决这个问题。

代码:

#include<stdio.h>
int find(int m, int n, int count);
int fight(int m, int n);
void show();
int queen[8][8] = { 0 };   //数字下标为棋盘位置,数值为第几个棋子,0代表无棋子
int methods = 0;     //统计总共有多少种情况

int main() {
	find(0, 0, 1);
	printf("总共有:%d", methods);
	return 0;
}

//从m行 n列寻找   第count个  
int find(int m, int n,int count) {
	if (n == 8) {
		m++;
		n = 0;
	}
	int q;                   //q为当前列
	for (q = n; q < 8; q++) {
		if (fight(m, q) == 0) {
			queen[m][q] = count;
			goto NEXT;         //计算下一个棋子
		}
	}
	for (m++; m < 8; m++) {
		for (q = 0; q < 8; q++) {
			if (fight(m, q) == 0) {
				queen[m][q] = count;
				goto NEXT;
			}
		}
	} 
		return 0;     //查找到最后一个都没找到,则停止查找
	NEXT:
	if (count == 8) {
		show();
		methods++; 
	}
	if (count < 8) {
		
		find(m, q+1, count+1);   //向后查找
		
	}
	queen[m][q] = 0;      //重新查找当前棋子
	find(m, q+1, count);   
}

//判断是否会相互攻击   攻击返回1 不会攻击返回0
int fight(int m, int n) {
	for (int q = 0; q < 8; q++) {
		if (queen[m][q] > 0)
			return 1;
	}
	for (int q = 0; q < 8; q++) {
		if (queen[q][n] > 0)
			return 1;
	}
	for (int a = m, b = n; a<=7&&a>=0&&b<=7&&b>=0; a++, b++) {
		if (queen[a][b] > 0)
			return 1;
	}
	for (int a = m, b = n; a <= 7 && a >= 0 && b <= 7 && b >= 0; a++, b--) {
		if (queen[a][b] > 0)
			return 1;
	}
	for (int a = m, b = n; a <= 7 && a >= 0 && b <= 7 && b >= 0; a--, b++) {
		if (queen[a][b] > 0)
			return 1;
	}
	for (int a = m, b = n; a <= 7 && a >= 0 && b <= 7 && b >= 0; a--, b--) {
		if (queen[a][b] > 0)
			return 1;
	}
	return 0;
}

//将当前棋盘展示
void show() {
	for (int m = 0; m < 8; m++) {
		for (int n = 0; n < 8; n++) {
			printf("%d ", queen[m][n]);
		}
		printf("\n");
	}
	printf("\n");
}

总结(回溯算法):

本问题的解题思路使用的是典型的回溯算法。我们可以将该算法思路类比为走迷宫。我们可以先在所有岔路口上左转,一直走到路的尽头。若此方法无法走到终点,我们回到上一个选择点,选择一条不同的路,依此类推。这种方法可以在不出现重复的情况下走完所有的路,并在计算机中可以很好的通过函数嵌套的方式( 子问题与原始问题为同样的事)实现。

拓展:

数独问题

另:

基于C++,使用位运算的版本(具有更高的计算效率和更低的内存占用量),支持棋盘大小<32*32的解法(思路可参考力扣 N皇后问题 官方解法):

#include <iostream>

class Solution
{
public:
    // n为棋盘大小
	int totalNQueens(int n)
	{
		n_ = n;
		return putQueen(0, 0, 0, 0);
	}

	// 若不能放置,对应bit位置为1, 否则为0
	int putQueen(int row, unsigned int column, unsigned int diagonal_lr, unsigned int diagonal_rl)
	{
		if (row == n_)
		{
			return 1;
		}
		else
		{
			int answer_num = 0;
			unsigned int choice = column | diagonal_lr | diagonal_rl;


			for (int i = 0; i < n_; i++)
			{
				if (choice % 2 == 0)
				{
					answer_num += putQueen(row + 1, column | (1 << i),
					                       (diagonal_lr | (1 << i)) >> 1,
					                       (diagonal_rl | (1 << i)) << 1);
				}
				choice >>= 1;
			}
			return answer_num;
		}
	}

	int n_;
};


int main()
{
	Solution solution;
	std::cout << solution.totalNQueens(4) << std::endl; // 2

    // 8皇后问题
	std::cout << solution.totalNQueens(8) << std::endl; // 92

	std::cout << solution.totalNQueens(9) << std::endl; // 352
}
  • 12
    点赞
  • 80
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值