回溯:八皇后问题(蒟蒻)

 
 
 

在一个n×n的棋盘上放置n个国际象棋中的皇后,要求所有的皇后之间都不形成攻击。请你给出所有可能的排布方案数。

输入格式

一个整数n

输出格式

方案数

经典的回溯题目。因为对于八皇后问题我们很难找到能够快速得到解的方法(嗯,那些10行写完的速度出门右拐)。所以我们采取枚举法。

皇后的攻击特性是,同行,同列,同一对角线。那么不妨先人为规定第k个皇后在第k行,这样就可以根据皇后的列号求解。

我们先把第1个皇后放在第1行第一列,然后再逐行递归。而这个搜索过程实际是在一棵搜索树上进行的

嗯,树太大不放出来就,百度据说一堆?

然后一个比较关键的问题是如何处理对角线

 

就比如说这样一个节点,如果用x表示行,y表示列,这个节点的右对角线就可以用

x-y+n

来表示,因为在这样一条对角线上,所以的点的横纵坐标都满足x-y+n

我们权且把它叫做这个点的右对角线

那么右对角线怎么办呢?

以这个点为例,同样设行用x表示,列用y表示,那么这个点的右对角线就是

x+y

所以在这条对角线上的点的坐标都满足这个式子。

那么接下来的问题就简单了,随便dfs,瞎标记就行了

代码如下:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 bool h[70], leftx[70], rightx[70];//h表示列,即不在同一列,leftx表示左对角线,rightx表示右对角线
 4 int n, ans = 0;
 5 
 6 inline int read() {
 7     int x = 0, y = 1;
 8     char ch = getchar();
 9     while(!isdigit(ch)) {
10         if(ch == '-') y = -1;
11         char ch = getchar();
12     }
13     while(isdigit(ch)) {
14         x = (x << 1) + (x << 3) + ch - '0';
15         ch = getchar();
16     }
17     return x * y;
18 }
19 
20 void dfs(int row) {//row表示行
21     if(row == n){
22         ans++;
23         return;
24     }
25     for(int i = 0; i < n; i++) {
26         if(!(h[i] || leftx[row+i] || rightx[row-i+n])) {//左对角线上,row+i是一个定值,所以在这条对角线上的数的行列和均为row+i,row-i+n同理
27             h[i] = leftx[row+i] = rightx[row-i+n] = 1;
28             dfs(row + 1);//在返回上一层前回溯才能放置新皇后
29             h[i] = leftx[row + i] = rightx[row - i + n] = 0;//返回上一层时初始化
30         }
31     }
32 }
33 int main()n {
34     n = read();
35     memset(h, 0, sizeof(h));
36     memset(leftx, 0, sizeof(leftx));
37     memset(rightx,0,sizeof(rightx));
38     dfs(0);
39     cout << ans << '
';
40     return 0;
41 
42 }
View Code

而在usaco上,还有一道和这个差距不是很大的题,就差了几行代码

 

可以看出无非就是按照上面那个思路让在递归的前3层输出所有点的列,开一个数组记录一下列就好了

dfs代码如下:

 

原文地址:https://www.cnblogs.com/ywjblog/p/8119587.html