n皇后问题

题目:在N*N格的格子上摆放N个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法?

 

分析:

①N*N个格子的棋盘可以用二维数组表示,但可用一个长度为n的一维数组,每个皇后坐标表示为 ( i , v[ i ] ) ,这样更为节省空间。

②任意两个皇后不能处于同一行、同一列或同一斜线上,则能得出每两个皇后( x1,y1 )(x2,y2)满足

             (x1-x2)==(y1-y2) 且 x1 != x2 , y1 != y2

③当使用一维数组进行存放皇后坐标时,已能满足  x1 != x2,只需判断另外两种条件即可

 

思路:

将摆放一次皇后分为两个步骤: 

1、开始放置第 t 个皇后

2、判断第 t 个皇后是否能被安全放下

  当 “第 t 个皇后能被安全放下” ,则 “开始放置第 t+1 个皇后" 

  当  ” 第 t 个皇后不能被安全放下 “,该摆法行不通,则更换t-1皇后位置,重新判断 t-1 个皇后是否能够安全放下

不断地放置皇后,当   “开始放置n+1个皇后”  时,已经放置了n个皇后,即寻找到一种摆法。

 

实现:

  方法一(递归回溯):

创建一个 Nqueen类 存放参数

 1 class Nqueen
 2 {
 3 private:
 4     int n;                          // 棋盘大小,皇后数量
 5     int sum;                       // 摆法数量
 6     vector<int>v;                  // 一维数组存放皇后坐标
 7 public:
 8     Nqueen(int x);                   // 构造函数,动态的调整一维数组长度
 9     void queen(int t);               // 开始放置第t个皇后
10     bool is_safe(int t);             // 判断第t个皇后是否能被放下 
11     void print();                   // 输出答案
12 };

 

构造函数:

1 Nqueen::Nqueen(int x)
2 {
3     n = x;
4     sum = 0;
5     v.assign(x+1,0);
6 }

 

开始放置第t个皇后

 1 void Nqueen::queen(int t)
 2 {
 3     if (t > n)                           //若要放置的皇后数量大于n时,已经放置了n个皇后,摆法加一
 4     {
 5         sum++;
 6     }
 7     else
 8     {
 9         for (int i = 1; i <= n; i++)     //遍历该行每一个坐标
10         {
11             v[t] = i;                    //暂存一个坐标
12             if (is_safe(t))             //判断第 t 个皇后是否能被安全放下
13                 queen(t + 1);           //第t个皇后放置完毕,开始放置第t+1个皇后
14         }
15     }
16     return;
17 }

 

判断第 t 个皇后是否能被安全放下

1 bool Nqueen::is_safe(int t)
2 {
3     for (int i = 1; i < t; i++)
4     {
5         if (v[i] == v[t] || abs(i - t) == abs(v[i] - v[t]))
6             return false;
7     }
8     return true;
9 }

 

输出答案函数

1 void Nqueen::print()
2 {
3     cout << sum << endl;
4 }

 

主函数

1 int main()
2 {
3     int n;
4     cin >> n;
5     Nqueen q(n);
6     q.queen(1);
7     q.print();
8     return 0;
9 }

 

 

  方法二:(迭代回溯):

思想上,迭代回溯与递归回溯的思想是一致的,都是回溯法但各有优劣。

  递归的方法简单易懂,但执行时需要不断地调用栈空间,占用内存大。且每一次调用函数都会出现开销。

  迭代的方法不易理解,代码逻辑复杂,优点是效率高,不管深度多大在内存空间上都不会有太大的变化。

代码上,两种方法的区别在 queen()函数中 。

 1 void Nqueen::queen()
 2 {
 3     v[1] = 0;
 4     int t = 1;
 5     while (t > 0)
 6     {
 7         v[t] += 1;                           //更换皇后坐标
 8         while (v[t] <= n && !is_safe(t))    //判断该行是否能安全放下皇后
 9             v[t]++;
10         if (v[t] <= n)    //若能安全放下皇后
11         {
12             if (t == n)
13                 sum++;  //可行解加一
14             else
15                 v[++t] = 0; // 判断下一行
16         }
17         else
18             t--; // 不能安全放下皇后,回溯判断上一个行皇后位置
19     }
20     return;
21 }

 

以下是在vs2019下执行的数据,方便大家校对。

1-1

2-0

3-0

4-2

5-10

6-4

7-40

8-92

9-325

10-724

11-2680

12-14200

 

以上是自己练题的个人笔记分享

另外我还在刷题平台上看到有耗时更低的方法无法查看,希望大家不吝赐教,分享下思路。

posted @ 2020-06-04 20:12  lmiz  阅读(188)  评论(0)    收藏  举报