[USACO1.5] 八皇后 Checker Challenge 题解

[USACO1.5] 八皇后 Checker Challenge

题目描述

一个如下的 \(6 \times 6\) 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。

上面的布局可以用序列 \(2\ 4\ 6\ 1\ 3\ 5\) 来描述,第 \(i\) 个数字表示在第 \(i\) 行的相应位置有一个棋子,如下:

行号 \(1\ 2\ 3\ 4\ 5\ 6\)

列号 \(2\ 4\ 6\ 1\ 3\ 5\)

这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。
并把它们以上面的序列方法输出,解按字典顺序排列。
请输出前 \(3\) 个解。最后一行是解的总个数。

输入格式

一行一个正整数 \(n\),表示棋盘是 \(n \times n\) 大小的。

输出格式

前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。

样例 #1

样例输入 #1

6

样例输出 #1

2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4

提示

【数据范围】
对于 \(100\%\) 的数据,\(6 \le n \le 13\)

题目翻译来自NOCOW。

USACO Training Section 1.5

(一)读懂题目

(Who) 关键词

6×6棋盘
六个棋子
每行、每列
每条对角线
只有一个

(What) 关键词之间关键联系:

满足每行每列每个对角线只有一个棋子的棋局就是一种解法

(How) 思路:

(1)

分析:第一反应使用深度优先搜索去做,枚举每一行,对本次摆放的棋子的每一列和每一个对角线都标上记号

(2)

分析:我们可以运用标记数组,bool类型来进行标记

(3)

分析:重要的是对角线的标记问题,但经过观察可以发现,对角线不是i+j相等就是i-j+8相等,所以可以利用这个特性来进行标记

(二)分析时间+空间复杂度

时间复杂度:O(n)
空间复杂度:O(n)

(三)代码实现

#include<iostream>
#include<cstdio>
using namespace std;
int ans,n;//ans是用来记录输出次数,题目只要求输出3次 
int a[15];//每一行 
bool b[15],c[40],d[40];//标记数组,b数组标记那一列,c和d数组标记对角线 
void print()//打印函数 
{
	for(int j=1;j<=n;j++)
	{
		printf("%d ",a[j]);
	}
	puts("");
	return;
}
void dfs(int i)//重点:深搜dfs 
{
	if(i>n)//如果一种情况成立(i已经遍历完每一列所有位置) 
	{
		ans++;//记录+1 
		if(ans<=3)//如果<=3才输出,否则就是+1而已 
		{
			print();
		}
		return;
	}
	for(int j=1;j<=n;j++)//枚举每一列 
	{
		if(!b[j]&&!c[i+j]&&!d[i-j+n])//如果这个点没有被其他皇后给攻击到 
		{
			//标记ing …… 
			b[j]=true;
			c[i+j]=true;
			d[i-j+n]=true;
			a[i]=j;
			dfs(i+1);//继续深搜
			//取消标记,回溯ing…… 
			b[j]=false;
			c[i+j]=false;
			d[i-j+n]=false;
		}
	}
	return;
}
int main(){
	scanf("%d",&n);
	dfs(1);//记得从1开始 
	printf("%d\n",ans);
	return 0;
}

(四)总结反思

本题就是著名的八皇后问题,最初由国际西洋棋棋手马克斯·贝瑟尔于1848年提出的问题,是回溯算法的典型案例。
然后就是被许多人又改成了许多版本(N皇后、K皇后、皇后游戏、还是N皇后)……
呃,正事——
本题考察的是我们对与搜索的掌握,但对于本题而言,深搜dfs的回溯还是更适合枚举方案的
所以最后也是运用了dfs进行作答
AC~
posted @ 2024-07-30 10:17  Atserckcn  阅读(222)  评论(0)    收藏  举报