n皇后问题之回溯法与分支限界法

简述

使用回溯法解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;
}
  • 6
    点赞
  • 63
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

乔布斯砸牛顿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值