一、问题描述

            在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

       二、回溯法一句话概括

            从一条路往前走,能进则进,不能进则退回来,换一条路再试。

       三、八皇后问题分析

            首先注意到每一行都要放置一个皇后,所以数据表示可以使用一个数组S,S[i]表示第i行第S[i]列放置了皇后。

            回溯的前进条件:在i行,找到一个位置可以放置皇后,则开始为第i+1行找寻可以放置皇后的位置。

            返回的情形:1、进行到第i行,但没有找到可以放置皇后的位置,那么跳回第i-1行,修改该行的皇后位置。

                             2、进行到第8行,找到一种放置皇后的解法,返回第7行,修改第7行的皇后的位置,以找寻更多的解。

 

      四、程序实现<C++ VS2012>

           1、递归程序实现如下:      

 1 #include<iostream>
 2 #include<cmath>
 3 using namespace std;
 4 #define N 8
 5 
 6 int *s; //存储已放置的皇后位置,s[i]表示第i行第s[i]列放置了皇后
 7 int now = 0;
 8 int m = 0;
 9 
10 int canPut(int i , int j)
11 {
12     //和已经放置的皇后位置进行比较
13     for(int k = 0;k < now;k++)
14         if(s[k] == j || abs(k-i)==abs(s[k]-j))
15             return 0;
16     return 1;
17 }
18 
19 void backTrack()
20 {
21     for(int i = 0;i < 8;i++)
22     {
23         if(now > 7)
24         {
25             //输出这一种结果
26             m++;
27             for(int h = 0;h < 8;h++)
28                 cout << s[h];
29             cout <<endl;
30             return;
31         }
32         if(canPut(now,i))
33         {
34             //可以放置
35             s[now] = i;
36             now++;
37             backTrack();
38             now--;
39         }
40     }
41 }
42 
43 int main()
44 {
45     s = new int[N];
46     backTrack();
47     cout << m;
48     return 0;
49 }
View Code

           2、非递归程序实现如下:

 1 #include <iostream>
 2 #define N 8
 3 using namespace std;
 4 
 5 int s[9]; //s[i]表示第i行,第s[i]列,这个位置 (i,s[i])  下标从1开始
 6 int sum;
 7 
 8 void init()
 9 {
10         for(int i = 1; i <= 9; i++)
11                 s[i] = 0;
12 }
13 
14 
15 bool canPut(int k)
16 {
17         //k表示当前进行到了第k行,要拿出前k-1行的皇后来和当前第k行的皇后比较
18         for(int i = 1; i < k; i++)
19                 if((abs(k - i) == abs(s[k] - s[i])) || s[i] == s[k])
20                         return false;//1.是否在同一斜线;2.是否位于同一列
21         return true;
22 }
23 
24 void Backtrack()
25 {
26       s[1] = 0; //第一行,结合下面s[k]++
27       int k = 1;
28       while(k > 0)
29       {
30           s[k]++;
31           while(s[k] <= N && !canPut(k))
32               s[k]++;
33           //找到了放置第k行皇后的位置
34           if(s[k] <= N)
35           {
36               if(k == N)
37               {
38                   sum++;
39               }
40               else
41               {
42                   k++;
43                   s[k] = 0;
44               }
45           }
46           else
47           {
48               //没找到放置第k行皇后,则跳回第k-1行
49               k--;
50           }
51 
52       }
53 
54 }
55 
56 int main()
57 {
58         sum = 0;
59         Backtrack();
60         cout<<sum;
61         return 0;
62 }
View Code

      五、online judge上类似的题目<可以用来测试代码>

           百练2754:http://bailian.openjudge.cn/practice/2754

           这个就是把皇后的放置解法保存下来之后,求一下就行了。

           这个是我的AC代码:

 1 #include <iostream>
 2 #include<sstream>
 3 #include<cmath>
 4 #define N 8
 5 using namespace std;
 6 
 7 int ss[9]; //ss[i]表示第i行,第ss[i]列,这个位置 (i,ss[i])  下标从1开始
 8 int res[92];//存放92个解的数组
 9 int index = 0;
10 
11 void storeResult()
12 {
13     stringstream str;
14     for(int i = 1;i <= N;i++)
15     {
16         str<<ss[i];
17     }
18     str >> res[index];
19     index++;
20 }
21 
22 bool canPut2(int k)
23 {
24         //k表示当前进行到了第k行,要拿出前k-1行的皇后来和当前第k行的皇后比较
25         for(int i = 1; i < k; i++)
26                 if((abs(k - i) == abs(ss[k] - ss[i])) || ss[i] == ss[k])
27                         return false;//1.是否在同一斜线;2.是否位于同一列
28         return true;
29 }
30 
31 void Backtrack2()
32 {
33       ss[1] = 0; //第一行,结合下面s[k]++
34       int k = 1;
35       while(k > 0)
36       {
37           ss[k]++;
38           while(ss[k] <= N && !canPut2(k))
39               ss[k]++;
40           //找到了放置第k行皇后的位置
41           if(ss[k] <= N)
42           {
43               if(k == N)
44               {
45                   //找到一组解
46                   storeResult();
47               }
48               else
49               {
50                   k++;
51                   ss[k] = 0;
52               }
53           }
54           else
55           {
56               //没找到放置第k行皇后,则跳回第k-1行
57               k--;
58           }
59 
60       }
61 
62 }
63 
64 int main()
65 {
66     int times = 0;
67     cin >> times;
68     int *p = new int[times];
69 
70     for(int i = 0;i < times;i++)
71         cin >>p[i];
72 
73     Backtrack2();
74 
75     for(int i = 0;i < times;i++)
76         cout << res[(p[i]-1)]<<endl;
77     return 0;
78 }
View Code

     六、感想

          自己通过自己独立分析问题,自己独立编程实现,最后在百练上找到这个题目顺利AC,这个节奏还是很好的~

          另外,感觉八皇后问题真的是典型的回溯法的应用。

          最后,YZY,我想你!~

      

posted on 2014-03-13 23:43  Oloo  阅读(134)  评论(0)    收藏  举报