POJ 3239 -- Solution to the n Queens Puzzle
Time Limit: 1000MS | Memory Limit: 131072K | |||
Total Submissions: 3872 | Accepted: 1419 | Special Judge |
Description
The eight queens puzzle is the problem of putting eight chess queens on an 8 × 8 chessboard such that none of them is able to capture any other. The puzzle has been generalized to arbitrary n × n boards. Given n, you are to find a solution to the n queens puzzle.
Input
The input contains multiple test cases. Each test case consists of a single integer n between 8 and 300 (inclusive). A zero indicates the end of input.
Output
For each test case, output your solution on one line. The solution is a permutation of {1, 2, …, n}. The number in the ith place means the ith-column queen in placed in the row with that number.
Sample Input
8
0
Sample Output
5 3 1 6 8 2 4 7
Source
解空间树是一棵排列树。
回溯法排列树的框架为:
回溯法的复杂度如下所示:
高复杂度导致了一个必然的结果——超时。
1 #include<iostream> 2 #include<cstdlib> 3 using namespace std; 4 int x[310]; 5 int isSolved = 0; 6 bool Constraint(int t) 7 {///新加入的为x[t],所以只需判断x[t]与前t-1个棋子是否在同一条斜线上 8 for(int j=1;j<t;j++) 9 { 10 if(abs(t-j) == abs(x[t] - x[j])) 11 return 0; 12 } 13 return 1; 14 } 15 16 void BackTrack(int t,int n) 17 {///t从1开始 18 if(t == n+1 || isSolved == 1) 19 {///只得到一个解 20 ///生成解 21 if(isSolved == 0) 22 { 23 for(int k = 1;k<=n;k++) 24 { 25 cout<<x[k]; 26 if(k != n) cout<<" "; 27 else cout<<endl; 28 } 29 } 30 isSolved = 1; 31 return; 32 }else{ 33 for(int i=t;i<=n;i++) 34 { 35 swap(x[t],x[i]); 36 if(Constraint(t) && isSolved == 0) 37 BackTrack(t+1,n); 38 swap(x[t],x[i]); 39 } 40 } 41 } 42 43 int main() 44 { 45 int n; 46 while(cin>>n && n!=0) 47 { 48 isSolved = 0; 49 for(int i=0;i<=n;i++) 50 x[i] = i; 51 BackTrack(1,n); 52 } 53 return 0; 54 }
M皇后问题: 在M×M格的国际象棋上摆放M个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上。
根据场景,又有三种衍生问题:
① 共有多少种摆法(即有多少种可行解)
② 求出所有可行解
③ 求任意一个可行解
问题① 属于 禁位排列 问题,目前是存在通项公式直接求解的。
问题② 属于 搜索 问题,在网上也有多种解法,主流是 回溯法(另有衍生的位运算变种算法),但不管如何优化,回溯法都有一个致命的问题:M值不能过大(一般M=30已是极限)。
问题③ 属于 问题② 的子集,因此很多人的切入点依然是回溯法,也有启发式算法的解法:如遗传算法、还有刘汝佳在《算法艺术与信息学竞赛》提出的启发式修补算法。启发式算法在M<10000左右都是可解的,但是因为启发式算法均存在随机性,收敛速度视不同的收敛因子而变化(我看过某篇论文称启发式算法在M=10000时的耗时等价于回溯法M=30的耗时)。
但早在1969年, 问题③ 的解就被E. J. Hoffman、J. C. Loessi 和R. C. Moore找到了潜在的数学规律,通过推导出数学公式,利用 构造法 使得该问题可在O(1) 的时间复杂度得到解。
2)构造法 目前网上流传的通解公式,则是为了便于编程,在坐标变换后得到的:
变换坐标后的求解公式如下:
① 当m mod 6 != 2 且 m mod 6 != 3时:
(A1):[2,4,6,8,...,m], [1,3,5,7,...,m-1] (m为偶)
(A2):[2,4,6,8,...,m-1], [1,3,5,7,...,m-2], [m] (m为奇)
② 当m mod 6 == 2 或 m mod 6 == 3时,
令 n= m / 2 (m为偶数) 或 n = (m-1)/2 (m为奇数)
(B1):[n,n+2,n+4,...,m], [2,4,...,n-2], [n+3,n+5,...,m-1], [1,3,5,...,n+1] (m为偶,n为偶)
(B2):[n,n+2,n+4,...,m-1], [1,3,5,...,n-2], [n+3,...,m], [2,4,...,n+1] (m为偶,n为奇)
(B3):[n,n+2,n+4,...,m-1], [2,4,...,n-2], [n+3,n+5,...,m-2], [1,3,5,...,n+1], [m] (m为奇,n为偶)
(B4):[n,n+2,n+4,...,m-2], [1,3,5,...,n-2], [n+3,...,m-1], [2,4,...,n+1], [m] (m为奇,n为奇)
上面有六条解序列:
一行一个序列(中括号是我额外加上的,以便辨认子序列)
第i个数为j,表示在第i行j列放一个皇后.
子序列与子序列之间的数序是连续关系(无视中括号就可以了), 所有子序列内j的递增值为2
1 #include<iostream> 2 using namespace std; 3 void Print(int x,int y) 4 { 5 for(int t = x;t<=y;t+=2) 6 { 7 cout<<t; 8 if(t != y) cout<<" "; 9 } 10 } 11 int main() 12 { 13 int m; 14 while(cin>>m && m != 0) 15 { 16 if(m%6 != 2 && m %6 !=3) 17 { 18 if(m%2 == 0)//m为偶数 19 { 20 Print(2,m);cout<<" ";Print(1,m-1);cout<<endl; 21 }else{ 22 Print(2,m-1);cout<<" ";Print(1,m);cout<<endl; 23 } 24 }else{ 25 int n = m/2; 26 if(m%2==0 && n%2==0) 27 { 28 Print(n,m);cout<<" ";Print(2,n-2);cout<<" ";Print(n+3,m-1);cout<<" ";Print(1,n+1);cout<<endl; 29 }else if(m%2 == 0 && n%2 == 1) 30 { 31 Print(n,m-1);cout<<" ";Print(1,n-2);cout<<" ";Print(n+3,m);cout<<" ";Print(2,n+1);cout<<endl; 32 }else if(m%2 == 1 && n%2 == 0) 33 { 34 Print(n,m-1);cout<<" ";Print(2,n-2);cout<<" ";Print(n+3,m-2);cout<<" ";Print(1,n+1);cout<<m;cout<<endl; 35 }else{ 36 Print(n,m-2);cout<<" ";Print(1,n-2);cout<<" ";Print(n+3,m-1);cout<<" ";Print(2,n+1);cout<<m;cout<<endl; 37 } 38 } 39 40 } 41 return 0; 42 }