八皇后问题 | Queens Problem | C/C++实现

问题描述

所谓八皇后问题,是指在8×8的国际象棋棋盘上放置8个皇后,保证任意2个皇后都无法相互攻击的问题。国际象棋中的皇后可以向8个方向移动任意格

现已在棋盘上摆放了k个皇后,且这k个格子的位置已给出。请编写一个程序,根据给出的k个有皇后的格子,输出已摆放8个皇后的国际象棋棋盘

输入:
第1行输入整数k。接下来k行输入已放有皇后的格子,每个格子用2个整数r、c表示。r、c分别为从0开始的国际象棋棋盘的行、列编号
输出:
输出表示8×8国际象棋棋盘的字符串,放有皇后的格子用"Q"表示,其他用"."表示
限制:
每个输入有唯一解。

输入示例

2
2 2
5 3

输出示例

......Q.
Q.......
..Q.....
.......Q
...Q....
.Q......
....Q...

讲解

最直接的方法就是穷举出8个皇后的所有摆放方法,然后依次检查其是否满足题中条件。棋盘共有8×8=64格,每次要选择8个格放皇后,因此总共有 C 64 8 = 4426165368 C^8_{64}=4426165368 C648=4426165368种组合。就算考虑到2个皇后无法同时出现在1行,即每行只能有一个皇后,那也有 8 8 = 16777216 8^8=16777216 88=16777216种组合。再加上2个皇后无法同时出现在同一列,也有 8 ! = 40320 8!=40320 8!=40320种组合

相对地,使用下述回溯法求解八皇后问题,要远远比遍历上述所有组合快得多:

在第1行的任意位置摆放皇后
在第2行中,选择不会被第1行皇后攻击的格子摆放第2个皇后

前 i 行放好 i 个皇后且保证它们不会相互攻击后,在第(i + 1)行寻找不会被任意一个皇后攻击的格子,摆放第(i + 1)个皇后
如果不存在满足上述条件的格子,则返回第 i 行继续寻找下一个不会被攻击的格子。如果不存在满足该条件的格子,则继续返回(i - 1)行

像这样,系统地尝试所有可能得出正确解的状态,当发现当前状态得不到解时中断搜索并返回(以当时中断的位置为起点)上一状态继续搜索,这样的手法称为回溯。图的深度优先搜索就是基于回溯的算法

在八皇后问题中,为记录格子是否会被其他皇后攻击,我们需要准备下列数组变量。这里的N=8
row[N]:如果row[x]为NOT_FREE,则x行受到攻击
col[N]:如果col[x]为NOT_FREE,则x列受到攻击
dpos[2N-1]:如果dpos[x]为NOT_FREE,则斜向左下的x列受到攻击
dneg[2N-1]:如果dneg[x]为NOT_FREE,则斜向右下的x列受到攻击

只要row[i]、col[i]、dpos[i+j]、dneg[i-j+N-1]中有任意一个是NOT_FREE,格子(i, j)就会受到攻击。也就是说,当row[i]、col[i]、dpos[i+j]、dneg[i-j+N-1]全都为FREE时,皇后可以放置在该格子中

AC代码如下

#include<iostream>
#include<cassert>
using namespace std;

#define N 8
#define FREE -1
#define NOT_FREE 1

int row[N], col[N], dpos[2 * N - 1], dneg[2 * N -1];

bool X[N][N];

void initialize() {
	for(int i = 0; i < N; i++) { row[i] = FREE, col[i] = FREE; }
	for(int i = 0; i < 2 * N - 1; i++) { dpos[i] = FREE; dneg[i] = FREE; }
} 

void printBoard() {
	for(int i = 0; i < N; i++) {
		for(int j = 0; j < N; j++) {
			if(X[i][j]) {
				if(row[i] != j) return;
			}
		}
	}
	for(int i = 0; i < N; i++) {
		for(int j = 0; j < N; j++) {
			cout<<( (row[i] == j) ? "Q" : ".");
		}
		cout<<endl;
	}
}

void recursive(int i) {
	if(i == N) {//成功放置皇后 
		printBoard(); return;
	}
	
	for(int j = 0; j < N; j++) {
		//如果(i, j)受到其他皇后攻击,则忽略该格子
		if(NOT_FREE == col[j] ||
		   NOT_FREE == dpos[i + j] ||
		   NOT_FREE == dneg[i - j + N -1]) continue;
		//在(i, j)放置皇后
		row[i] = j; col[j] = dpos[i + j] = dneg[i - j + N -1] = NOT_FREE;
		//尝试下一行
		recursive(i + 1);
		//(i, j)拿掉摆放在(i,j)的皇后
		row[i] = col[j] = dpos[i + j] = dneg[i - j + N -1] = FREE;
		//皇后放置失败 
	}
}

int main(){
	initialize();
	
	for(int i = 0; i < N; i++)
		for(int j = 0; j < N; j++) X[i][j] = false;
		
	int k; cin>>k;
	for(int i = 0; i < k; i++) {
		int r,c; cin>>r>>c;
		X[r][c] = true;
	}
	
	recursive(0);
	
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值