八皇后问题(英文:Eight queens),是由国际西洋棋棋手马克斯·贝瑟尔于1848年提出的问题,是回溯算法的典型案例。
问题表述为:在8×8格的国际象棋上摆放8个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。
一开始想的可以用穷举法,暴力列举8^8次方种可能
能做,但是效率低。
然后深入思考下:
满足皇后可以让皇后落叫的点有这样三个特点
1、这个点所在行没有其他皇后
2、这个点所在列没有其他皇后
3、这个点所在左右对角线没有其他皇后
基于这三点,大概的思路就出来了
1、用深度优先搜索的方法,搜索行
2、开一个bool数组看一下对应点的列是不是有棋子
3、判断两个对角线是否有棋子
训练之前前两点都容易实现,唯独第三点,没训练之前我恐怕会直接开两个for循环把对角线每个点都逐个看一下,浪费效率不说,代码也会写的很难看
所以我们来看下左右对角线的特点
首先对于一个n * n表格来说,左右对角线数目相等且等于2*n-1条
左右对角线规模搞定了
然后就是规律
一个点确定的时候,他所在的左右对角线也确定了
我们来看这样一个例子
这是棋盘中的一条右对角线
可以看到其上的坐标点的规律是横纵坐标和相等
也就是说,一个点的横纵坐标相加决定了他在哪条右对角线上
左对角线呢?
可以看到,坐标的差是一样的,这条左对角线还是最长的,差还是零
仔细总结下就是
左对角线:对角线上的坐标横纵坐标作差相等,且差为0时最长(等于n)
右对角线:对角线上的坐标横纵坐标作和相等,且和为n+1时最长
(棋盘是正方形)
拓展一下:如果是长方形表格:
这个长为6宽为3的长方形表格一共有四条长度为三的对角线
那么可以类比一下
左对角线:对角线上的坐标横纵坐标做差相等,且0<=差<=长-宽时相等最长的左对角线共有长-宽+1条
右对角线:对角线上的坐标横纵坐标做和相等,且宽+1<=和<=长+1时相等的最长的右对角线长-宽+1条
可以看出最长左对角线=最长右对角线数,都等于长-宽+1条
(做完题写博客的时候自己找纸和office表格花了半小时才总结出来的。。。果然学知识要有耐心)
那么这样我们就可以用规模为2*n-1的一个数组来存储左右对角线了
(左left右right左left右right左left右right左left右right左left右right。。。。)
因为左对角线
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
char chessBorad[100][8][8];//100个棋盘
char temp[8][8];
int leftd[15];//左对角线
int rightd[15];//右对角线
bool jud[8];
int ans;//记录一共有几种结果
void copy() {//把temo存到chessBorad里
for(int i=0;i<8;i++)
for (int j = 0; j < 8; j++) {
chessBorad[ans][i][j] = temp[i][j];
}
ans++;
}
void dfs(int n) {
if (n == 8) {
copy();
return;
}
for (int i = 0; i < 8; i++) {
if (!jud[i] && !leftd[n + i] && !rightd[7 + (n - i)]) {
//列没有,两个对角线没有
//本身就是在一行只放一个的回溯搜索
//英语太差,左右不分了写反了。。。不过不影响交代吗就是了。。。
temp[n][i] = 'Y';//代表皇后,调试的时候比干巴巴的01看着舒服
jud[i] = true;
leftd[i + n] = true;//对角线标记上
rightd[7 + (n - i)] = true;
dfs(n + 1);//搜索下一行
temp[n][i] = 'O';//状态还原
jud[i] = false;
leftd[i + n] = false;
rightd[7 + (n - i)] = false;
}
}
}
int main() {
memset(chessBorad,'O',sizeof(chessBorad));
memset(temp,'O',sizeof(temp));
dfs(0);
cout << ans;//ans等于92
}
这样准备工作就做完了(把所有92种情况存到chessBorad里了,然后按照输入把92个棋盘一个个看一遍就是)
代码如下
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<string.h>
#include<stdio.h>
#include<math.h>
using namespace std;
typedef long long ll;
char chessBorad[100][8][8];
char temp[8][8];
int leftd[15];//左对角线
int rightd[15];//右对角线
bool jud[8];
int ans;//记录一共有几种结果
void copy() {
for (int i = 0; i < 8; i++)
for (int j = 0; j < 8; j++) {
chessBorad[ans][i][j] = temp[i][j];
}
ans++;
}
void dfs(int n) {
if (n == 8) {
copy();
return;
}
for (int i = 0; i < 8; i++) {
if (!jud[i] && !leftd[n + i] && !rightd[7 + (n - i)]) {
temp[n][i] = 'Y';
jud[i] = true;
leftd[i + n] = true;
rightd[7 + (n - i)] = true;
dfs(n + 1);
temp[n][i] = 'O';
jud[i] = false;
leftd[i + n] = false;
rightd[7 + (n - i)] = false;
}
}
}
void out(ll x,ll y) {
ll temp=0;
ll Ans = 1;
printf("SOLN COLUMN\n");
printf(" # 1 2 3 4 5 6 7 8\n\n");
while (temp < ans) {
if(chessBorad[temp][y-1][x-1] == 'Y') {
printf("%2lld ", Ans++);
for (ll i = 0; i < 8; i++) {
for (ll j = 0; j < 8; j++) {
if (chessBorad[temp][i][j] == 'Y') {
printf(" %lld", j + 1);
continue;
}
}
}
printf("\n");
}
temp++;
}
}
int main() {
ll n, x, y;
memset(chessBorad, 'O', sizeof(chessBorad));
memset(temp, 'O', sizeof(temp));
dfs(0);
cin >> n;
while (n--) {
cin >> x >> y;
out(x, y);
if(n) printf("\n");//这输出格式扣得也太细了
}
return 0;
}
放张图给自己养养眼