题目来源:《信息学奥赛一本通》例5.4
题目描述
要在国际象棋棋盘(\(8 \times 8\) 的棋盘)中放 \(8\) 个皇后,使任意两个皇后都不能互相吃。(提示:皇后能吃同一行、同一列、同一对角线的任意棋子。)
输出格式
输出一个整数,用于表示八皇后问题的放置方案。
题目分析
首先我们用 \((x,y)\) 来表示棋盘上第 \(x\) 行第 \(y\) 列的格子的坐标。
那么,两个皇后 \((x_1,y_1)\) 和 \((x_2,y_2)\) 会互相攻击当且仅当满足如下条件之一:
- 在同一行:\(x_1 = x_2\)
- 在同一列:\(y_1 = y_2\)
- 在同一对角线:\(x_1-x_2=y_1-y_2\) 或 \(x_1-x_2=y_2-y_1\) ,化简一下就是坐标差的绝对值相等,即: \(\vert x_1-x_2 \vert = \vert y_1-y_2 \vert\)
我们可以用深度优先搜索来解决这道题。
我们可以发现的是,要想在 \(8 \times 8\) 的棋盘上放置 \(8\) 个皇后,每一行都必须且只能放置一个皇后,所以我开一个数组 ans[]
,ans[id]
用于表示在第 id
行放置的皇后的列号。
然后我开一个函数 f(id)
,用于表示:当前正准备在第 id
行放置一个皇后。然后我从 \(1\) 到 \(8\) 去遍历列号 \(i\) ,如果 \((id,i)\) 能放,则我尝试性地在 \((id,i)\) 位置放上皇后,然后递归调用 f(id+1)
, 直到 id>8
(说明找到了一种放置方案)为止。
那么,我怎么去判断 \((id,i)\) 这个位置能否放置一个皇后呢?我们搜索的顺序是从第 \(1\) 行一直到第 \(8\) 行的,所以当我要放置第 \(id\) 行的皇后的时候,肯定已经在第 \(1\) 到 \(id-1\) 行放置好了前 \(id-1\) 个皇后。所以我只需要从 \(1\) 到 \(id-1\) 去遍历行号 \(j\) ,比较一下第 \(j\) 行放置的皇后 \((j,ans[j])\) 和我现在想要放的皇后 \((id,i)\) 是否会相会攻击即可。
如果这 \(id-1\) 个皇后都和 \((id,i)\) 位置没有冲突,则说明 \((id,i)\) 这个位置是可以放的,那我尝试性地在 \((id,i)\) 位置放上皇后,并递归地进行下一步搜索 f(id+1)
。
实现代码如下:
#include <bits/stdc++.h>
using namespace std;
int ans[9], cnt; // ans[i]用于记录第i行皇后列号,cnt用于记录方案数
// attack函数用于判断(x1,y1)和(x2,y2)两个点是否会互相攻击
// 返回true:会互相攻击到;返回false:不会互相攻击到
bool attack(int x1, int y1, int x2, int y2) {
return x1==x2||y1==y2||abs(x1-x2)==abs(y1-y2);
}
// f函数用于在第id行尝试性地放一个i,然后递归地去id+1行放
void f(int id) {
if (id > 8) { // 说明前8行已经放好了
cnt ++; // 找到一个方案,cnt++
return; // 程序可直接返回
}
for (int i = 1; i <= 8; i ++) { // 尝试在第id行第i列放皇后
bool flag = true; // flag用于标识是否能放
for (int j = 1; j < id; j ++) {
if (attack(id, i, j, ans[j])) { // (id,i)和(j,ans[j])冲突
flag = false; // 将flag设为false标识不能放
break;
}
}
if (flag) { // 如果循环结束flag仍为true说明i能放
ans[id] = i; // 能放就先放上
f(id+1); // 然后递归进行下一行的放置
}
}
}
int main() {
f(1); // 从第1行开始放
cout << cnt << endl; // 输出方案数
return 0;
}
原文地址:https://www.cnblogs.com/zifeiynoip/p/11450705.html