八皇后问题

 目录

问题分析

过程模拟

问题回答

八皇后问题非递归实现

八皇后问题递归方式


问题分析

 八皇后问题:高斯于1850年提出的问题。

       在8*8的棋盘上放置八个皇后,任意两个皇后都不能处于同一行、列或者同一斜线上。

    分析:

              首先要解决的两个问题:

                 1. 如何表示棋盘?

                 2. 如何获取皇后的位置信息,进而判断是否相互攻击?

    解决思想:

        解题思想就是问题回溯法。

过程模拟

当某个皇后探测到最后一列,而其后还有皇后没有摆放完全的时候,就需要回溯;

将本次探测皇后的上一个皇后向后再探测一个位置,继续下面的皇后的从0开始到最大列的探测;

如果回溯的过程中,直到第一个皇后到最大列位置,还不满足条件,也就是下次的回溯是-1(不存在)的时候,表示皇后的位置探测完毕;

如果在这个探测的过程中,没有输出满足条件的皇后位置序列,表示当前的棋盘和皇后序列不存在。

问题回答

Q1:

表示皇后,可以采用两种方式。

第一种是使用二维数组,用值来标识当前皇后在本位置存在。 如:int chess[N][N];

第二种是使用一位数组,用下标表示皇后的编号,用存放的值表示该皇后在棋盘中列的位置。如 int chess[N];

Q2:

假设皇后i和皇后j在棋盘上的位置是(i, Xi),(j, Xj);

不在同行:i≠j

不在同列:Xi≠Xj

不在同一条斜线上:|i-j|≠|Xi-Xj|  (也即是斜率的绝对值不等于1)

八皇后问题非递归实现

#include<iostream>
#include<cmath>
using namespace std;

const int N = 100;//假定最多能求的皇后问题
int x[N] = {-1};//这种方式下标表示皇后,值表示存放的列位置 


//判断第k个皇后放置是否发生冲突 
bool Success(int k){
	//这里判断和前面的皇后是否发生冲突,所以前面皇后的下标是0-(k-1) 
	for(int i=0;i<k;i++){
		//这里皇后的行for循环控制了的,所以可能行发生冲突 
		if(x[i]==x[k] || abs(k-i) == abs(x[i]-x[k])) 
			return false;//发生冲突 
	}
	return true;//不发生冲突 
} 
//打印n皇后问题的一个解 
void Print(int n){
	for(int i=0;i<n;i++){//行 
		for(int j=0;j<n;j++){//列 
			if(j==x[i]){   //x[i]中存放的就是列下标 
				cout<<"Q  ";
			}
			else{
				cout<<"0  ";
			}
		}
		cout<<endl;
	}
	cout<<endl; 
} 
void Queue(int n){
	int k=0, num=0; //num存放问题的解的个数
	while(k>=0){//前面分析过,回溯到-1的时候,表示探测完毕 
		x[k]++;   //下一列摆放皇后k   , 初始k=-1,故而开始探测的起始位置列是0 
		//方式皇后后,就需要判断是否发生冲突
		while(x[k]<n and !Success(k)){//发生冲突,就探测下一列 
			x[k]++; 
		} 
		//发生冲突会执行上面的,不发生冲突才执行下面 
		if(x[k]<n and k==n-1){ //k=n-1表示 最后一个皇后,x[k]<n表示位置有效 
			cout<<"第"<<++num<<"个解是:"<<endl;
			Print(n);
		} 
		else if(x[k]<n and k<n-1){//表示还有皇后没有找到位置 
			//不发生冲突,还有皇后没放置完,故而需要放置下一个皇后
			k++; 
		}else{
			//探测越界 , 大于n-1, 表示本行没有可以放置的位置,所以回溯上一个元素
			x[k--] = -1;  //回溯,并重新初始化上一个皇后所在的列下标 
		}
	} 
}



int main(void){
	Queue(4); 
	return 0;
}

 ---------------------------------------------------分割线-------------------------------------------------

结果截图:

八皇后问题递归方式

#include<iostream>
#include<cmath>
using namespace std;

const int N = 100;//假定最多能求的皇后问题
int chess[N][N]={0};
int n = 4;
bool Success(int k, int w){
	for(int i=0;i<k;i++){//检查判断的始终都是前面的放置好位置的皇后 
		for(int j=0;j<n;j++){
			if(chess[i][j]==1){
				if(k==i or j==w or abs(i-k)==abs(j-w))
					return false;
			}
		}
	}
	return true;
}
void Print(){
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			cout<<chess[i][j]<<" ";
		}
		cout<<endl;
	}
	cout<<endl;
}
int  num = 0;
void Queue(int row){
	if(row==n){//假设n=8,八皇后row取值是0-7,故而在递归到row=8时就需要输出,
	//然后退栈,再次row在调用的过程中row++,到row=8时输出,退栈,直到栈空 
		cout<<"第"<<++num<<"个八皇后序列输出:"<<endl;
		Print();
	}else{
		for(int j=0;j<n;j++){//模拟列 
			chess[row][j] = 1;
			if(Success(row, j)){//如果成功,进行下一行的探测放置 
				Queue(row+1); 
			} 
			chess[row][j]=0;//撤回棋子 
			//递归调用,撤回棋子在最后一步,则如果探测下一个位置满足
			//条件,直接进入下一个皇后的位置探测,不满足的时候,才会撤回
			//棋子,同样的,在退栈的过程中,如果不满足,会回退到j++
			//也就是上一个皇后进行下一个位置的探测,如此反复 
		}
	}
}



int main(void){
	Queue(0); 
	return 0;
}

 -------------------------------------------------------分割线-------------------------------------------------

结果截图:


Java版本:

public List<List<String>> solveNQueens(int n) {
    List<List<String>> result = new ArrayList<List<String>>();
    boolean[][] matrix = new boolean[n][n];
    backtrack(matrix, 0, result);
    return result;
}

private void backtrack(boolean[][] matrix, int row, List<List<String>> result) {
    int size = matrix.length;
    if(row == size){
        result.add(getResult(matrix));
    }else{
        for (int i = 0; i < size; i++) {
            if(!isValid(matrix, row, i)){
                continue;
            }
            matrix[row][i] = true;
            backtrack(matrix, row + 1, result);
            matrix[row][i] = false;
        }
    }
}

private List<String> getResult(boolean[][] matrix) {
    List<String> res = new ArrayList<String>();
    for (boolean[] booleans : matrix) {
        StringBuilder sb = new StringBuilder();
        for (boolean aBoolean : booleans) {
            sb.append(aBoolean ? 'Q' : '.');
        }
        res.add(sb.toString());
    }
    return res;
}

private boolean isValid(boolean[][] matrix, int row, int k) {
    for(int i=0;i<row;i++){//检查判断的始终都是前面的放置好位置的皇后
        for(int j=0;j<matrix.length;j++){
            if(matrix[i][j]){
                if(j==k || Math.abs(i - row)== Math.abs(j - k))
                    return false;
            }
        }
    }
    return true;
}

作者:无涯明月

发文时间:2018-11-17

  • 19
    点赞
  • 81
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

梦否

文章对你有用?不妨打赏一毛两毛

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

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

打赏作者

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

抵扣说明:

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

余额充值