N皇后问题
杭电OJ——ACM 2553.N皇后问题链接入口
问题描述
有一个n行n列的棋盘,你手里有个n个棋子(即皇后),每下一枚棋子,棋子所处的同行、同列、两条对角线,都不能再放置棋子了,如下图。现在,键盘交给你,写一个程序,计算n颗棋子在n*n棋盘上的放置方法总数。
算法思想:
这道题用到的算法思想是递归和回溯,接下来我简要讲一下我对递归和回溯的理解,如果有任何错误的地方或者更好的说法,请大家多多指正。
递归:递归就是一种不断调用自身函数的算法,举个最经典的例子:“从前有座山,山上有座庙,庙里有个老和尚在讲故事,讲的是什么呢?讲的是,从前有座山,山上有座庙。。。”,这样一种不断调用自身的算法。不过,递归算法必须要有一个结束条件。
递归算法的目的是:将难度规模为1的问题通过递归求出难度规模为n的问题,但前提是不同难度规模问题求解的方法一致。
回溯:回溯是一种从一个初始状态出发,不断往下一个最优状态探索,如果没办法再往下探索,或者是下一个可探索状态不是优解,就往回退一步,直到最后得出结果的算法。
在该题上的理解就是,如果皇后从第一行开始放第一个,记录下来限制条件,往下一行放下一个皇后,如果下一行由于与限制条件冲突,放不下,就需要回退到上一行的皇后,重新放置到该行的另一位置。
解题思路:
首先,出于对时间限制的考虑,先将1到10个的皇后数进行运算得到结果存放在结果数组内。然后,我们只要根据输入的皇后数,输出对应下标的数组结果输出即可。
其次,我们就可以开始构建算法。了,首先是递归算法,递归结束条件是“k>n”,每次递归的时候都是进入下一行的放置,就要k++,意思即是已经放置到了第几个皇后,以及是已经放置到了第几行,直到第n个放置完毕。
再说到回溯算法,每当递归一行放置成功时,都会记录下已被占据的行、列、对角线。如果,递归到下一行时,该行的所有位置都在已被占据的位置里,则都不符合条件,无法向下一行递归。这是就会回退了,回退到上一行重新放置,在上一行已被占据的行、列、对角线的记录清空。放置成功后,继续递归,再判断能不能继续递归到下一行,无法继续递归则回溯。
最后,就是说到:我们怎么记录被占领的行、列、主对角线、副对角线呢?由于每次递归都是向下一行继续放置,所以占据行不需记录。在这里,我定义了三个数组,分别记录被占据的列、主对角线、副对角线。占据列的记录容易做到,只需要你放置的时候,将循环的列数对应的下标数的数组元素赋为1即可。说到两条对角线,需要你去找到规律,将数组里对应下标的元素赋为1即可表明已被占领。我这里用的是 坐标法 去找对角线的规律,我们可以从下图看出不同位置的对角线有其规律:
主对角线的横纵坐标轴相加等于一个常数
副对角线的横纵坐标轴相减等于一个常数
C++版AC代码
#include<iostream>
using namespace std;
#include<math.h>
int n,sum,result[15],xflect[15],lflect[30],rflect[30];
void dfs(int k){
if(k>n){
sum++;
return ;
}
for(int i=1;i<=n;i++){
if(xflect[i]==0&&lflect[k+i]==0&&rflect[k-i+n]==0){
xflect[i]=1;
lflect[k+i]=1;
rflect[k-i+n]=1;
dfs(k+1);
xflect[i]=0;
lflect[k+i]=0;
rflect[k-i+n]=0;
}
}
}
int main(){
for(int i=1;i<=10;i++){
n=i;
sum=0;
dfs(1);
result[i]=sum;
}
cin>>n;
while(n!=0){
cout<<result[n]<<endl;
cin>>n;
}
return 0;
}
C++运行展示:
JAVA版AC代码
import java.util.Scanner;
class dfs{
int n,sum;
int result[] = new int[15];
int xflect[] = new int[15];
int lflect[] = new int[30];
int rflect[] = new int[30];
public dfs(){}
public void dfs1(int k){
if(k>n){
this.sum++;
return ;
}
for(int i=1;i<=n;i++){
if(xflect[i]==0&&lflect[k+i]==0&&rflect[k-i+n]==0){
xflect[i]=1;
lflect[k+i]=1;
rflect[k-i+n]=1;
this.dfs1(k+1);
xflect[i]=0;
lflect[k+i]=0;
rflect[k-i+n]=0;
}
}
}
}
class Main{
public static void main(String[] args) {
dfs d = new dfs();
Scanner scan =new Scanner(System.in);
for(int i=1;i<=10;i++){
d.n=i;
d.sum=0;
d.dfs1(1);
d.result[i]=d.sum;
}
d.n = scan.nextInt();
while(d.n!=0){
System.out.println(d.result[d.n]);
d.n = scan.nextInt();
}
scan.close();
}
}
JAVA运行展示: