一、问题描述
在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 }
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 }
五、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 }
六、感想
自己通过自己独立分析问题,自己独立编程实现,最后在百练上找到这个题目顺利AC,这个节奏还是很好的~
另外,感觉八皇后问题真的是典型的回溯法的应用。
最后,YZY,我想你!~
浙公网安备 33010602011771号