Time Limit:1000MS | Memory Limit:32768KB | 64bit IO Format:%I64d & %I64u |
Description
Input
Output
Sample Input
1 8 5 0
Sample Output
1 92 10
二、解题思路:
要解决N皇后问题,其实就是要解决好怎么放置这n个皇后,每一个皇后与前面的所有皇后不能在同一行、同一列、同一对角线,在这里我们可以以行优先,就是说皇后的行号按顺序递增,只考虑第i个皇后放置在第i行的哪一列,所以在放置第i个皇后的时候,可以从第1列判断起,如果可以放置在第1个位置,则跳到下一行放置下一个皇后。如果不能,则跳到下一列...直到最后一列,如果最后一列也不能放置,则说明此时放置方法出错,则回到上一个皇后向之前放置的下一列重新放置。此即是回溯法的精髓所在。当第n个皇后放置成功后,即得到一个可行解,此时再回到上一个皇后重新放置寻找下一个可行解...如此后,即可找出一个n皇后问题的所有可行解。
三、复杂度分析:
关于N皇后问题的复杂度问题可以说是众说纷纭了,自己也改变过好几次,刚开始以为棋盘是n行n列,所以理所当然应该是n^2,后来发现在每列选择可否放置的比较上又做了一次循环,所以应该是n^3,但想了很久,发现判断可否放置的时候不是每次都循环到n,它是根据皇后i的取值而变化的,所以复杂度应该是1/3 n^3左右,即是小于n^3的。
以下两种代码是超时的代码,但是解题的思路是正确的,如有不理解的参见注释。
两种方法主要是用到了回溯法求解,第二种方法是用二维数组进行优化。
#include <stdio.h> #include <string.h> const int N = 15; int C[N]; int tot; int n; void search(int cur) { if( cur == n) { tot++; }else { for(int i = 0; i < n; i++) { int ok = 1; C[cur] = i; //尝试把第cur行的皇后放到第i列 for(int j = 0; j < cur; j++) { //检查是否和前面的皇后冲突 if(C[cur] == C[j] || cur - C[cur] == j - C[j] || cur + C[cur] == j + C[j]) { //在同一行上 //在同一左斜对角线上 //在同一右斜对角线上 ok = 0; break; } } if(ok) { search(cur+1); } } } } int main() { while( scanf("%d",&n) != EOF && n) { tot = 0; memset(C,0,sizeof(C)); search(0); printf("%d\n",tot); } return 0; }
但是经过优化后还是超时,看来只能打表了。
#include <stdio.h> #include <string.h> const int N = 15; //int C[N]; int tot; int n; int vis[4][N*2]; void search(int cur) { if(cur == n) { tot++; }else { for(int i = 0; i < n; i++) { if(!vis[0][i] && !vis[1][cur+i] && !vis[2][cur-i+n]) { //当前行被访问 //当前行右对角线被访问 //当前行左对角线被访问 //C[cur] = i; //如果不用打印,数组C可以忽略 vis[0][i] = vis[1][cur+i] = vis[2][cur-i+n] = 1; search(cur+1); vis[0][i] = vis[1][cur+i] = vis[2][cur-i+n] = 0; } } } } int main() { while( scanf("%d",&n) != EOF && n) { tot = 0; memset(vis,0,sizeof(vis)); search(0); printf("%d\n",tot); } return 0; }
#include <stdio.h> int ans[10]={1,0,0,2,10,4,40,92,352,724}; int main() { int n; while(scanf("%d",&n) != EOF && n) { printf("%d\n",ans[n-1]); } return 0; }