搜索—八皇后问题(全网最详细版bushi)及n皇后问题

[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

分析

根据题目,我们可以知道棋盘上每一行每一列只能有一个棋子,所以说如果我们按行放置棋子,每行放置一个棋子就ok,同时还要保证这个放置的棋子的右上、正上、左上都没有棋子(因为按行数从上到下,只有上一行放过棋子),那这时就可以放下一行的棋子,同样要求,直到放置到最后一行。我们可以客观的想到一旦有一行找到一个棋子我们就先进行下一行的寻找,这样找一个的下一个后找“一个的下一个”的下一个,就是DFS了。题目要求输出前三个答案(按照字典序)因为我们棋子是从左列到右列找的,所以直接按照行号记录符合条件的棋子列号,就可以了。同样要注意的是:我们找到符合位置的棋子后也就意味着,有左上、正上、右上三个位置需要去标记,这时候才能接着找下一行,当我们结束了对这一行中这一个棋子位置的下几行的查找后,我们就要改变这一行中这个棋子的位置,这时候,因为棋子不在原来的位置了。那么对应的左上、右上、正上三个位置也需要取消标记。
关于左上、右上、正上这几个位置我们怎么去标记呢?首先,对于正上,到了下一行它的位置就是上一行所标记的,那么标记好的这个列号以后都不能再放棋子了,所以我们直接对col[i]进行标记;对于左上,为了已经保证放好棋子的左上到右下的每一行都不能放棋子,我们也要保证左上到右下的这些位置有一个固定的编号所以,我们对udg[u-i+n]进行标记;对于右上,原因同左上,对dg[i+u]进行标记。
下面是以4*4的棋盘为例

1 2 3 4
1 (1,1) (2,1) (3,1) (4,1)
2 (1,2) (2,2) (3,2) (4,2)
3 (1,3) (2,3) (3,3) (4,3)
4 (1,4) (2,4) (3,4) (4,4)

i+u后

1 2 3 4
1 1 3 4 5
2 3 4 5 6
3 4 5 6 7
4 5 6 7 8

i-u+n后

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

为什么是“i+u”和“i-u+n”呢?其实很好证明,我们以棋盘的首行为x轴,首列为y轴,沿x轴向上翻折,得到如图所示:



因为数组都为正所以u-i需要+n确保永远为真,同时这也这知道了i+u的范围是在2-2n间,u-i+n也是如此。

代码实现

#include <bits/stdc++.h>
using namespace std;
int n;
bool col[15],dg[30],udg[30];
int ans[15];
int sum = 0;
inline void dfs(int u)
{
	if(u > n){
		if(sum<3)
			{
				for(int i = 1 ; i <= n ;i ++)
					cout<<ans[i]<<" ";
				cout<<endl;
			}
		sum++;
	return;
	}
	for(int i = 1;i <= n;i ++)
		if(!col[i]&&!dg[i+u]&&!udg[u-i+n]){
				col[i] = dg[i+u] = udg[u-i+n] = 1;
				ans[u] = i;
				dfs(u+1);
				col[i] = dg[i+u] = udg[u-i+n] = 0;
			}

}
int main()
{
	cin>>n;
	dfs(1);
	cout<<sum;
	return 0;
}

n-皇后问题

直达原题

n−皇后问题是指将 n 个皇后放在 n×n 的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。
现在给定整数 n,请你输出所有的满足条件的棋子摆法。

输入格式

共一行,包含整数 n。

输出格式

每个解决方案占 n 行,每行输出一个长度为 n 的字符串,用来表示完整的棋盘状态。其中 . 表示某一个位置的方格状态为空,Q 表示某一个位置的方格上摆着皇后。每个方案输出完成后,输出一个空行。注意:行末不能有多余空格。输出方案的顺序任意,只要不重复且没有遗漏即可。

More Info

数据范围

1≤n≤9

输入样例:

4

输出样例:

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

分析

这个和八皇后问题是一样的,只不过符号化的输出每一中结果,别忘了对棋盘数组进行初始化就好

代码实现

#include <bits/stdc++.h>
using namespace std;
char chess[10][10] ;
char col[10] ,dg[30] , udg [30];
int n;
void dfs(int u)
{
    if(u > n)
    {
        for(int i = 1; i <= n;i ++)
        {
            for(int j = 1; j <= n; j++)
                cout<<chess[i][j];
            cout<<endl;
        }
        cout<<endl;
        return;
    }
    for(int i = 1; i <= n;i++)
    {
        if(col[i]!='Q'&&dg[i+u]!='Q'&&udg[u-i+n]!='Q')
           {
                 chess[u][i] = 'Q';
                 col[i] = dg[i+u] = udg [u-i+n] = 'Q';
                 dfs(u+1);
                 col[i] = dg[i+u] = udg [u-i+n] = '.';
                 chess[u][i] = '.';
           }

    }

}
int main()
{
    cin>>n;
    memset(chess,'.',sizeof(chess));//一种初始化方法,注意不能在全局作用域
    dfs(1);
    return 0;

}
posted @ 2023-08-04 22:25  LongDz  阅读(45)  评论(0)    收藏  举报