八皇后问题--递归加回溯

Posted 我是一颗大萝北

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了八皇后问题--递归加回溯相关的知识,希望对你有一定的参考价值。

一、问题描述

在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

二、问题分析

由于皇后们是不能放在同一行的, 所以可以去掉“行”这个因素,即第1次考虑把皇后放在第1行的某个位置, 第2次放的时候就不用去放在第一行了。第2次考虑把皇后放在第2行的某个位置,第3次考虑把皇后放在第3行的某个位置,这样依次去递归。每计算1行,递归一次,每次递归里面考虑8列,即对每一行皇后有8个可能的位置可以放。找到一个与前面行的皇后都不会互相攻击的位置, 然后再递归进入下一行。找到一组可行解即可输出,然后程序回溯去找下一组可靠解。

三、怎样判断当前皇后和已有皇后是否发生冲突呢?

  • 用一个一维数组来表示相应行对应的列,比如col[i]=j表示第i行的皇后放在第j列。一共有8列,所以我们要让c[r]依次取第1列,第2列……一直到第8列, 每取一次我们就去考虑,皇后放的位置会不会和前面已经放了的皇后有冲突:同行,同列,对角线
  • 由于已经 不会同行了,所以不用考虑同行这一点
  • 同列:col[r]==col[j]
  • 同对角线:同对角线有两种可能,即主对角线方向和副对角线方向。 主对角线方向满足,行之差等于列之差:r-j=col[r]-col[j]; 副对角线方向满足行之差等于列之差的相反数:r-j=col[j]-col[r]

四、代码框架

for循环给出8种情况(8列),可以看成一颗树的8个分支。每一个分支节点继续进行递归又出现8个分支,当然有的分支冲突就直接舍弃(如图中红色叉号),到了树的叶子结点就return。
在这里插入图片描述

void eight(int line){
	/*--递归出口---*/
	if(line==9){
	。。。
		return;
	}	

	/*---当列不冲突,对角线不冲突时,可以摆放----*/
	for(int j=1;j<=8;j++){  //尝试从第一列开始放 
	。。。	
			eight(line+1);  //递归摆放下一个皇后 
	}
} 
int main(){
	
	eight(1);

	return 0;
}

完整代码如下

#include<bits/stdc++.h>
using namespace std;
int output[9][9];
int col[9];  //1~8行,记录每一行的皇后在哪一列
int cnt=0;

void eight(int line){
	/*--递归出口---*/
	if(line==9){
		cnt++;
		/*输出*/
		for(int i=1;i<=8;i++){
			for(int j=1;j<=8;j++){
				cout<<output[i][j];
			}
			cout<<endl;
		}
		cout<<endl;
		return;
	}	
	
	/*---当列不冲突,对角线不冲突时,可以摆放----*/
	for(int j=1;j<=8;j++){  //尝试从第一列开始放 
		bool colli=false; 
		for(int index=1;index<=line-1;index++){  //看看跟之前的冲不冲突 
			if(j==col[index]||abs(line-index)==abs(j-col[index]))
			{
				colli=true;
		    }
		}
		if(colli==false){  //找到一个和之前皇后都不冲突的位置 
			col[line]=j;  //在这一行摆放一个皇后
			output[line][j]=1;
			eight(line+1);  //递归摆放下一个皇后 
			output[line][j]=0; //回溯!!!!!!!!!!!!!!!!!!!!!!!!! 
		}
	}
} 
int main(){
	memset(output,0,sizeof(output));
	eight(1);
	cout<<cnt<<"种布局"<<endl; 
	return 0;
}

四、注意手动回溯

递归是可以自己实现回溯的,但是呢由于我们定义了int output[9][9]来输出每一种布局,所以在回溯的时候要求抹掉上一次摆放的皇后。也就是要手动实现这一行

output[line][j]=0; //回溯!!!!!!!!!!!!!!!!!!!!!!!!! 

如果采用的不是int output[9][9]的方式来输出,那么可以不需要手动回溯,就像下面的代码
早前写的代码了,所以风格和上面的不太一样。
在这里插入图片描述

#include <iostream>
#include<stdio.h> 
using namespace std;

int col[20], n=8;//8皇后 
int count=0;//计数多少个布局 
void eightempress(int now);
int main(){
    eightempress(0);
    printf("%d种布局",count);
    return 0;
}
void eightempress(int now){
    if(now == n){
        for(int i=0; i<n; ++i){
            for(int j=0; j<n; ++j){
               if(j == col[i]) printf("1 ");//皇后 
                else printf("0 ");
            }
            cout<<endl;
        }
        cout<<endl;
        count++;
        return;
    }
    for(int i=0; i<n; ++i){
        col[now] = i;//从第一行开始尝试摆放皇后 
        int ok = 1;
        for(int j=0; j<now; ++j)//和之前的now-1个皇后比较 
            if(col[now]==col[j] || abs(now-j)==abs(col[now]-col[j]) ){//列冲突、对角线冲突 
                ok = 0;
                break;
            }
        if(ok) eightempress(now+1);
    }
}

以上是关于八皇后问题--递归加回溯的主要内容,如果未能解决你的问题,请参考以下文章

八皇后,回溯与递归(Python实现)

回溯法八皇后问题(递归和非递归)

算法——八皇后问题(递归回溯实现)

迷宫回溯和八皇后问题

八皇后问题的两个高效的算法(回溯与递归)

利用递归和回溯解决八皇后问题