N皇后问题
Time Limit:1000MS | Memory Limit:32768KB | 64bit IO Format:%I64d & %I64u |
Description
在N*N的方格棋盘放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在与棋盘边框成45角的斜线上。
你的任务是,对于给定的N,求出有多少种合法的放置方法。
你的任务是,对于给定的N,求出有多少种合法的放置方法。
Input
共有若干行,每行一个正整数N≤10,表示棋盘和皇后的数量;如果N=0,表示结束。
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;
}
AC代码
#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;
}