N皇后问题
咳咳,马上就要考试了,今天重新来复习一下N皇后问题,好好的整理下,嗯嗯,N皇后作为经典的回溯和剪枝应用,还是很好入门的。
行,那就先看题目吧,先把题目意思弄懂。
hdu 2553 “n皇后问题”
题目链接:点我了解题目(这是一个链接)
题目大意:
在N*N的方格棋盘放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在与棋盘边框成45角的斜线上。
你的任务是,对于给定的N,求出有多少种合法的放置方法。
输入样例:
1
8
5
0
输出样例:
1
92
10
- 咳咳,先看题目哈,题目要求在一个 N * N 的方格里面放置N个皇后,并且这N个皇后不同行、不同列、不同斜线。
- 不明白?我们来看看图吧,这里我们用四皇后来举个例子!
- 我们按照不同行不同列不同斜边的规则对四皇后问题进行依次扩展。
- 其实当n较小,人工都能算出来,但是当n稍稍大的时候就不是人工能搞的了,对于该图,用BFS和DFS都可以算出来,但是BFS相对于DFS代码过于繁琐,我们就用DFS来解决吧!
- 那么重点来了,我们要如何在扩展下一次节点的时候去除不符合条件的子节点呢?
- 接下来我们一步一步来解决!
- 我们设已经放好的皇后的坐标设置为 (i , j),下一个放置的不同行、不同列、不同斜线的新皇后的坐标为 (r , c)。那么根据不同行、不同列、不同斜线,我们可以建立以下剪枝规则:
- (1) 不同行,所以每行只有一个皇后,这个也不用写,每行就设置一个皇后就能满足不同行的要求。即:i != r。
- (2) 不同列,这里要注意以下,要求的是所有的皇后都不同列,所以我们要求,所有设立皇后列都不相同,即:j != c。
- (2) 不同斜线,我们假设一下,从 (i , j)往斜线走a步,那么新坐标 (r , c)就有四种情况,分别是左上(i-a , j-a)、右上(i+a , j-a)、左下(i-a , j+a)、右下(i+a , j+a),那么我们整合一下就是,abs(i-r)=a 、abs(j-c)=a。那么新皇后不在同一斜线上面,只需要满足:abs(i-r) != abs(j-c)即可。
- ok,到这里,剪枝规则便建立的差不多了。
- 然后题目输入输出样例可以看出来,是多组输入,因为对应的每一个N皇后输出的放置方式数量是一个固定的值,所以我们需要进行打表来算,这样可以避免重复运算,大大的降低算法的运行时间。
- 好滴,放代码!!!
#include<bits/stdc++.h>
using namespace std;
int col[12]={0}; //col数组用于存放第i行第col[i]列放置皇后。
int n,tot = 0; //设置全局变量,即可不用投值,直接调用
bool check(int r,int c){ //判断新皇后是否和放好的皇后发生冲突。
for(int i=0;i<r;i++){
//这里不需要判断是否同行,因为我们设置的每行只投放一个皇后,所以不需要判断,只判断是否同列或者同斜线即可。
if((col[i]==c)||(abs(col[i]-c)==abs(i-r)))return false;
}
return true;
}
void dfs(int r){
if(r==n){ //因为从0开始,当行数r达到n时,表示已经完成了
tot++;
return ;
}
for(int c=0;c<n;c++){
if(check(r,c)){ //判断是否可以放置,可以便放置,否则剪枝。
col[r]=c;
dfs(r+1); //继续放置下一行的新皇后
}
}
}
int main(){
int ans[12]={0};
for(n=0;n<=10;n++){ //对n皇后进行打表,然后结果存入ans数组
memset(col,0,sizeof(col)); //每次要进行初始化
tot=0;
dfs(0); //DFS
ans[n]=tot; //存放
}
while(cin >> n){
if(n==0)return 0;
cout << ans[n] << endl;
}
return 0;
}
- 代码很短,却极具有智慧,嘿嘿。
- 这时候也许有人要问了,这里只是将N皇后的数量给求出来,我要图呢,怎么搞?
- 这里得说一下哈!
- 其实也是很简单的,我们直接在dfs里面进行输出就行了,当 r== n 的时候,就表示出现了一个可以放置n个皇后的方法了,那么只要放 r== n 我们进行输出,因为col数组,表示的是,第 i 行第 col[i] 列,那么我们就可以根据col数组,在 r==n的时候进行输出了。
- 代码如下:
#include<bits/stdc++.h>
using namespace std;
int col[12]={0};
int n,tot = 0;
bool check(int r,int c){
for(int i=0;i<r;i++){
if((col[i]==c)||(abs(col[i]-c)==abs(i-r)))return false;
}
return true;
}
void dfs(int r){
if(r==n){ //达成条件即可输出。
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(col[i]!=j)cout << " ." ; //这里只需要根据col数组对j进行判断即可输出皇后了,col数组表示的是第i行第col[i]列放置皇后,那么只需要判断col[i]==j的时候放置皇后即可,其余情况直接不放。
else cout << " X";
}
cout << endl;
}
cout << endl;
cout << "-------------分割线-------------" << endl;
cout << endl;
tot++;
return ;
}
for(int c=0;c<n;c++){
if(check(r,c)){
col[r]=c;
dfs(r+1);
}
}
}
int main(){
while(cin >> n && n){
memset(col,0,sizeof(col));
tot=0;
dfs(0);
cout << tot << endl;
}
return 0;
}
- 这样就可以做到输出图了,效果如下:
- 咳咳,okok,今天的n皇后就到这里吧。玩去了。。。。。