八皇后问题 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
}
  • 14
    点赞
  • 80
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
八皇后问题是一个经典的回溯算法问题,在 C 语言中可以使用递归实现。具体实现步骤如下: 1.定义一个二维数组 chessboard 存储棋盘状态。 2.定义一个 bool 类型的函数 check 判断当前位置是否合法。 3.定义一个递归函数 solve,实现回溯算法。 4.在 solve 函数中,使用双重循环遍历棋盘的每一个位置,如果当前位置合法,则将皇后放置在该位置,并将棋盘状态更新。 5.在递归函数中,如果已经成功放置了 8 个皇后,则说明找到了一组解,输出该解。 6.如果当前位置不合法,则回溯到上一个位置,继续遍历下一个位置。 下面是 C 语言的八皇后问题实现代码: ```c #include<stdio.h> #include<stdbool.h> #define N 8 // 定义棋盘大小 int chessboard[N][N]; // 定义棋盘 // 判断当前位置是否合法 bool check(int row, int col) { int i, j; // 检查同一列是否有皇后 for (i = 0; i < row; i++) { if (chessboard[i][col] == 1) { return false; } } // 检查左上方是否有皇后 for (i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--) { if (chessboard[i][j] == 1) { return false; } } // 检查右上方是否有皇后 for (i = row - 1, j = col + 1; i >= 0 && j < N; i--, j++) { if (chessboard[i][j] == 1) { return false; } } return true; } // 递归求解八皇后问题 void solve(int row) { int i, j; if (row == N) { // 如果已经成功放置了 8 个皇后,则输出该解 for (i = 0; i < N; i++) { for (j = 0; j < N; j++) { printf("%d ", chessboard[i][j]); } printf("\n"); } printf("\n"); return; } for (i = 0; i < N; i++) { // 遍历棋盘的每个位置 if (check(row, i)) { // 如果当前位置合法,则放置皇后 chessboard[row][i] = 1; solve(row + 1); chessboard[row][i] = 0; // 回溯到上一个位置 } } } int main() { int i, j; for (i = 0; i < N; i++) { for (j = 0; j < N; j++) { chessboard[i][j] = 0; // 初始化棋盘 } } solve(0); // 从第一行开始求解八皇后问题 return 0; } ``` 在上述代码中,我们使用了一个二维数组 chessboard 存储棋盘状态,其中 0 表示该位置没有皇后,1 表示该位置有皇后。在 check 函数中,我们检查当前位置是否与已放置的皇后冲突。在 solve 函数中,我们使用双重循环遍历棋盘的每一个位置,如果当前位置合法,则将皇后放置在该位置,并将棋盘状态更新。如果已经成功放置了 8 个皇后,则说明找到了一组解,输出该解。如果当前位置不合法,则回溯到上一个位置,继续遍历下一个位置。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值