2020年7月17日icpc集训J题:UVA-750八皇后问题

八皇后问题(英文: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;
}

放张图给自己养养眼
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值