简述
使用回溯法解n皇后的思路还是比较简单地,不过代码比较长。
题一(回溯法):
Description
n-皇后问题要求在一个n*n的棋盘上放置n个皇后,使得它们彼此不受“攻击”。观察表明n-皇后问题的解存在垂直对偶性,请修改教材算法NQeens,令x[0]=1,2,……,[n/2],使得只求其中不对称的那些解。
Input
n(4<=n<=32)的值。
Output
不对称的那些解。
Sample Input
4
Sample Output
1 3 0 2
HINT
#include<iostream>
using namespace std;
int t=0;
int flag=0;
int abs(int k){
if(k>=0)
return k;
else
return -k;
}
bool place(int k, int i, int *x){
//判定两个皇后是否在同一列上
for(int j=0;j<k;j++){
if( (x[j]==i) || abs(x[j]-i)==abs(j-k) ){
return false;
}
}
return true;
}
/*flag用来标记入口
当n为偶数时,x[0] = 0~n/2-1
当n为奇数时,考虑x[i]中的某一个位于对称轴上,此时用到flag来标记当前这一层递归是由上一层x[i]="中值",
这个“中值”代表的是:例如5皇后中x[i]=2 (2位于 【0 1 2 3 4】 的中间)
*/
void NQueens(int k, int n,int *x,int flag){
for(int i=0;i<n;i++){
if(i>(n/2) && flag==0) return;
if(i==(n/2) && n%2==0 && flag==0) return;
if(place(k,i,x)){
x[k]=i;
if(k==n-1){
if(t!=0) cout<<"\n";
for(i=0;i<n;i++){
cout << x[i]; //输出一个可行解
if(i!=n-1) cout<<" ";
}
t++;
}else{
if(i==(n/2) && n%2!=0 && flag==0){
NQueens(k+1,n,x,0);
}
else
NQueens(k+1,n,x,1); //深度优先进入下一层
}
}
}
}
//将第一层拿出
void NQueens(int n, int *x){
NQueens(0,n,x,0);
}
int main(){
int n;
int x[100];
cin >> n;
NQueens(n,x);
return 0;
}
题二(分枝限界法):
Description
在 n×n格的棋盘上放置彼此不受攻击的 n(3<n<11)个皇后。按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n后问题等价于在 n×n格的棋盘上放置n个皇后,任何 2个皇后不放在同一行或同一列或同一斜线上。设计一个解 n 后问题的队列式分枝限界法,计算在 n×n个方格上放置彼此不受攻击的n个皇后的一个放置方案。
Input
1 个正整数n。
Output
n个皇后的第一种放置方案(行和列号都从1开始,每个数字后带有一个空格)。
Sample Input
4
Sample Output
2 4 1 3
HINT
必须使用分枝限界法,否则不计成绩
代码一:含注释和部分辅助理解的输出
#include<iostream>
#include<queue>
#include<math.h>
using namespace std;
struct Node{
int i,j;
int mark=0;
Node *parent;
};
int main(){
int n;
int i,j,k;
int a[100];//暂存第一组解
cin >> n;
queue<Node *> q;
Node *s;
s = new Node();
s->mark=-1;
s->i=-1;
s->j=-1;
s->parent=NULL;
//将第一行加入队列
for(i=0;i<n;i++){
Node *p;
p = new Node();
p->i = 0;
p->j = i;
p->parent = s;
q.push(p);
}
// /*
//在第2-n行中寻找满足n皇后限定条件的点
Node *p, p1;
int row;
while(!q.empty()){
//取出一个元素
p = q.front();
//寻找其满足条件的子节点
row = p->i+1; //下一行
int flag=1;
cout << "取出的元素:"<<p->i<<","<<p->j<<"\n";
for(j=0;j<n;j++){
flag=1;
p = q.front();
//判断(row,j)是否与parent(i,j)相斥
while(p->parent!=NULL){
cout << "compare"<< "("<< p->i<<","<<p->j <<")与("<< row<<","<< j <<")" <<":"<< abs(j-p->j) << " "<< abs(row-p->i)<<" mark:"<<p->parent->mark<<"\n";
if((p->j==j) || (abs(j-p->j)==abs(row-p->i))){
flag=0;
break;
}
p = p->parent;
}
cout<<"======\n";
//可参与构成n皇后的解
if(flag==1){
p=new Node();
p->i=row;
p->j=j;
cout <<"("<<row<<","<< j<<")\n";
p->parent=q.front();
q.push(p);
if(p->i==n-1){
i=0;
while(p->parent!=NULL){
a[p->i] = p->j+1;
cout <<"["<<p->i<<","<<p->j<<"] <= ";
p = p->parent;
}
cout << "\n";
for(i=0;i<n;i++){
cout << a[i]<<" ";
}
return 0;
}
}
}
q.pop();
cout<<"=====================`\n";
}
// */
return 0;
}
再遇n皇后
过了很长时间再去做n皇后问题,又有了新的做法
暴力回溯
#include<stdio.h>
#include<iostream>
#include<cstring>
#include<math.h>
#include <vector>
using namespace std;
vector<vector<string> > results;
vector<int> temp;
int count = 0;
bool conflict(int px1, int py1, int px2, int py2) {
if (px1 == px2 || py1 == py2) return true;
if (abs(px1 - px2) == abs(py1 - py2)) return true;
return false;
}
vector<string> build(int n) {
vector<string> str_list;
for (int i = 0; i < n; i++) {
string s;
for (int j = 0; j < n; j++) {
if (temp[i] == j) {
s += 'X';
} else {
s += 'O';
}
}
str_list.push_back(s);
}
return str_list;
}
int nqueen(int n, int row) {
if (temp.size() == n) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (i != j) {
if (conflict(i, temp[i], j, temp[j])) {
return 0;
}
}
}
}
// printf("%d %d, %d %d\n", temp[0], temp[1], temp[2], temp[3]);
count++;
results.push_back(build(n));
return 1;
}
for (int i = 0; i < n; i++) {
temp.push_back(i);
nqueen(n, row + 1);
temp.pop_back();
}
return -1;
}
int main() {
int n=9;
nqueen(n, 0);
for (int i = 0; i < results.size(); i++) {
for (int j = 0; j < n; j++) {
cout << results[i].front()<<endl;
results[i].erase(results[i].begin());
}
printf("\n");
}
cout << "共"<<count<<"种."<<endl;
return 0;
}
2.回溯剪枝
#include<stdio.h>
#include<iostream>
#include<cstring>
#include<math.h>
#include <vector>
using namespace std;
vector<vector<string> > results;
vector<int> temp;
int count = 0;
bool conflict(int px1, int py1, int px2, int py2) {
if (px1 == px2 || py1 == py2) return true;
if (abs(px1 - px2) == abs(py1 - py2)) return true;
return false;
}
vector<string> build(int n) {
vector<string> str_list;
for (int i = 0; i < n; i++) {
string s;
for (int j = 0; j < n; j++) {
if (temp[i] == j) {
s += 'X';
} else {
s += 'O';
}
}
str_list.push_back(s);
}
return str_list;
}
int nqueen(int n) {
for (int i = 0; i < n; i++) {
int flag = 0;
//判断当前与temp里的是否冲突,冲突则剪纸
for (int j = 0; j < temp.size(); j++) {
if (conflict(j, temp[j], temp.size(), i)) {
flag = 1;
break;
}
}
if (flag==1) continue;
//若都不冲突,且temp.size()==n-1,则是要找的答案
temp.push_back(i);
if (temp.size() == n) {
results.push_back(build(n));
count++;
temp.pop_back();
continue;
}
nqueen(n);
temp.pop_back();
}
return -1;
}
int main() {
int n=4;
nqueen(n);
for (int i = 0; i < results.size(); i++) {
for (int j = 0; j < n; j++) {
cout << results[i].front()<<endl;
results[i].erase(results[i].begin());
}
printf("\n");
}
cout << "共"<<count<<"种."<<endl;
return 0;
}