八皇后问题

在这里插入图片描述
八皇后问题(英文:Eight queens),是由国际象棋棋手马克斯·贝瑟尔于1848年提出的问题,是回溯算法的典型案例。

问题表述为:在8×8格的国际象棋上摆放8个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。如果经过±90度、±180度旋转,和对角线对称变换的摆法看成一类,共有42类。

放置第i个(行)皇后的算法为:

int search(int i){
	int j;
    for(j=1;j<=8;j++){
        if(本行本列允许放置皇后)
            放置第i个皇后;
            对放置皇后的位置进行标记;
            if(i==8)输出//已经放完8个皇后
            else search(i+1);//放置第i+1个皇后
        	释放标记,尝试下一个位置是否可行;
    }
}

【算法分析】

显然问题的关键在于如何判定某个皇后所在的行、列、斜线上是否有别的皇后。

可以从矩阵的特点上找到规律,如果在同一行,则行号相同;如果在同一列,则列号相同;

如果在/斜线上,则行列值之和相同,如果在\斜线上,则行列值之差相同。

考虑到每行有且只有一个皇后,设一维数组a[1…8]表示皇后的放置:第i行皇后放在第j列,用a[i]=j来表示,即下标是行数,内容是列数。例如:a[3]=5就表示第3个皇后在第3行第5列上。

判断皇后是否安全,即检查同一列、同一对角线是否已有皇后,建立标志数组b[1…8]控制同一列只能有一个皇后,若两皇后在同一对角线上,则其行列坐标之和或行列坐标之差相等,故亦可建立标志数组c[1…16],d[-7…7]控制同一对角线上只能有一个皇后。
【参考程序】

#include<bits/stdc++.h>
using namespace std;
bool d[17]={0},b[9]={0},c[17]={0};
int sum=0,a[9];
int search(int);
int print();
int main(){
	search(1);
	cout<<sum<<endl;
	return 0;
}
int search(int i){
	
	int j;
	for(j=1;j<=8;j++){
		if((!b[j])&&(!c[i+j])&&(!d[i-j+7])){//!b[j]--->表示不能在同一列
		//!c[i+j]--->表示不能在正对角线上
		//!d[i-j+7]--->表示不能在反对角线上
			a[i]=j;
			b[j]=1;
			c[i+j]=1;
			d[i-j+7]=1;
			if(i==8)print();
			else search(i+1);
			b[j]=0;
			c[i+j]=0;
			d[i-j+7]=0;
		}
	}
}
int print()
{	
	int i;
	sum++;
	for(int i=1;i<=8;i++)
		cout<<a[i]<<" ";
	cout<<endl;	
}

八皇后问题(来源:openjudge)

1700:八皇后问题

总时间限制: 10000ms 内存限制: 65536kB

描述

在国际象棋棋盘上放置八个皇后,要求每两个皇后之间不能直接吃掉对方。

输入

无输入。

输出

按给定顺序和格式输出所有八皇后问题的解(见Sample Output)。

【算法分析】

本题依旧可以使用上面的代码去完成,但是会出现超时问题。此时需要注意:(1)把不需要返回值的函数重新定义为void;(2)使用printf编写输出。

这样时间上会加快不少。

scanf和printf比cin和cout要快很多,有时候会因为这个超时,所以虽然不知道为什么,但以后还是尽量用scanf和printf吧(还是要根据情况,如果数据比较大比较多就用省时的)(各有各的优势)

【参考程序】

深搜从0开始

参考地址

    #include<iostream>
    using namespace std;
    int cnt = 1;//方案计数器
    int chess[8][8];//棋盘情况
    bool judge(int row, int col);//判断某个位置是否可放
    void print();//输出函数
    void dfs(int row) {
    	if (row >= 8) {//深搜结束条件
    		print();
    		cnt++;
    		return;
    	}
    	for (int j = 0; j <= 7; j++) {
    		if (judge(row, j)) {
    			chess[row][j] = 1;
    			dfs(row + 1);//深搜
    			chess[row][j] = 0;//回溯,表示换个位置试试,之前的位置就当作没走咯
    		}
    	}
    }
    int main() {
    	dfs(0);
    	return 0;
    }
    bool judge(int row, int col) {
    	for (int i = 0; i <= 7; i++)//判断列
    		if (chess[i][col] == 1) return false;
    	for (int i = row, j = col; i >= 0 && j >= 0; i--, j--) {//判断左上对角线
    		if (chess[i][j] == 1) return false;
    	}
    	for (int i = row, j = col; i >= 0 && j <= 7; i--, j++) {//判断右上对角线
    		if (chess[i][j] == 1) return false;
    	}
    	return true;
    }
    void print() {
    	cout << "No. " << cnt << endl;
    	for (int j = 0; j <= 7; j++) {//!!!按列输出!!!
    		for (int i = 0; i <= 7; i++) {
    			if (chess[i][j] == 1) cout << "1" << " ";
    			else cout << "0" << " ";
    		}
    		cout << endl;
    	}
    }

深搜从1开始

    #include<bits/stdc++.h>
    using namespace std;
    bool a[9][9],b[9],c[17],d[17];
    int tot=0;
    void search(int);
    
    int main(){
    	search(1);
    	return 0;
    }
    void search(int i){//i代表皇后所在的行
    	int j;//代表皇后所在的列
    	for(j=1;j<=8;j++){//放置8个皇后
    		if((!b[j])&&(!c[i+j])&&(!d[j-i+7])){//!b[j]--->表示不能在同一列
    		//!c[i+j]--->表示不能在正对角线上
    		//!d[i-j+7]--->表示不能在反对角线上
    			a[i][j]=1;
    			b[j]=1;
    			c[i+j]=1;
    			d[j-i+7]=1;
    			if(i==8){
    				tot++;
    				cout<<"No. "<<tot<<endl;
    				for(int x=1;x<=8;x++){//按列输出皇后
    					for(int y=1;y<=8;y++)
    							cout<<a[y][x]<<" ";	
    					cout<<endl;
    				}
    			}
    			else search(i+1);
    			b[j]=0;//回溯
    			c[i+j]=0;
    			d[j-i+7]=0;
    			a[i][j]=0;
    		}
    	}
    	
    }

第三种解法(与上面第一个案例相似)

	#include<bits/stdc++.h>
    using namespace std;
    int a[9],b[9],c[17],d[17],tot=0;
    void search(int);
    int print();
    int main(){
    	search(1);
    	return 0;
    }
    void search(int i){
    	int j;
    	for(j=1;j<=8;j++){
    		if((!b[j])&&(!c[i+j])&&(!d[j-i+7])){//!b[j]--->表示不能在同一列
    		//!c[i+j]--->表示不能在正对角线上
    		//!d[i-j+7]--->表示不能在反对角线上
    			a[i]=j;
    			b[j]=1;
    			c[i+j]=1;
    			d[j-i+7]=1;
    			if(i==8){
    				tot++;
    				printf("No. %d\n",tot);
    				for(int x=1;x<=8;x++){//按列输出皇后
    					for(int y=1;y<=8;y++)
    							if(a[y]==x){
    							printf("1 ");
    							}	
    							else printf("0 ");
    					printf("\n");}
    			}
    			else search(i+1);
    			b[j]=0;
    			c[i+j]=0;
    			d[j-i+7]=0;
    		}
    	}
    }
    

八皇后(来源:openjudge)

1756:八皇后

总时间限制: 1000ms 内存限制: 65536kB

描述

会下国际象棋的人都很清楚:皇后可以在横、竖、斜线上不限步数地吃掉其他棋子。如何将8个皇后放在棋盘上(有8 * 8个方格),使它们谁也不能被吃掉!这就是著名的八皇后问题。对于某个满足要求的8皇后的摆放方法,定义一个皇后串a与之对应,即a=b1b2…b8,其中bi为相应摆法中第i行皇后所处的列数。已经知道8皇后问题一共有92组解(即92个不同的皇后串)。给出一个数b,要求输出第b个串。串的比较是这样的:皇后串x置于皇后串y之前,当且仅当将x视为整数时比y小。

输入

第1行是测试数据的组数n,后面跟着n行输入。每组测试数据占1行,包括一个正整数b(1 <= b <= 92)

输出

输出有n行,每行输出对应一个输入。输出应是一个正整数,是对应于b的皇后串。

样例输入

2
1
92

样例输出

15863724
84136275

【算法分析】

提示:可以使用最上面案例的解法完成。

首先,如果每一次都去进行深搜的话一定会出现超时问题,所以最好的办法是先进行一次深搜,然后在深搜的过程中存储每一种方案,这样在进行输出是只需要从保存的方案中拿出来就可以了,时间复杂度为O(1)。

如果在保存过程中直接保存为八位数字,可以在输出时直接按位取数,这样就不再需要去进行八次循环输出了。

【参考程序】

    #include<bits/stdc++.h>
    using namespace std;
    bool d[17]={0},b[9]={0},c[17]={0};
    int sum=0,a[9],queen[95][9];
    void search(int);
    void print();
    int n,x;
    int main(){
    	cin>>n;
    	search(1);
    	for(int i=1;i<=n;i++){
    		cin>>x;
    		for(int j=1;j<=8;j++)
    			cout<<queen[x][j];
    		cout<<endl;
    	}
    	return 0;
    }
    void search(int i){//i代表皇后所在的行
    	int j;
    	for(j=1;j<=8;j++){
    		if((!b[j])&&(!c[i+j])&&(!d[i-j+7])){//!b[j]--->表示不能在同一列
    		//!c[i+j]--->表示不能在正对角线上
    		//!d[i-j+7]--->表示不能在反对角线上
    			a[i]=j;
    			b[j]=1;
    			c[i+j]=1;
    			d[i-j+7]=1;
    			if(i==8)print();
    			else search(i+1);
    			b[j]=0;
    			c[i+j]=0;
    			d[i-j+7]=0;
    		}
    	}
    	
    }
    
    	
    void print()
    {	
    	int i;
    	sum++;
    	for(int i=1;i<=8;i++)
    		queen[sum][i]=a[i];
    }	

P1219 [USACO1.5]八皇后 Checker Challenge(来源:洛古)------n皇后问题

题目描述

一个如下的 6 × 6 6 \times 6 6×6 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。

上面的布局可以用序列 2   4   6   1   3   5 2\ 4\ 6\ 1\ 3\ 5 2 4 6 1 3 5 来描述,第 i i i 个数字表示在第 i i i 行的相应位置有一个棋子,如下:

行号 1   2   3   4   5   6 1\ 2\ 3\ 4\ 5\ 6 1 2 3 4 5 6

列号 2   4   6   1   3   5 2\ 4\ 6\ 1\ 3\ 5 2 4 6 1 3 5

这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。
并把它们以上面的序列方法输出,解按字典顺序排列。
请输出前 3 3 3 个解。最后一行是解的总个数。

输入格式

一行一个正整数 n n n,表示棋盘是 n × n n \times n n×n 大小的。

输出格式

前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。

样例 #1

样例输入 #1

6

样例输出 #1

2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4

提示

【数据范围】
对于 100 % 100\% 100% 的数据, 6 ≤ n ≤ 13 6 \le n \le 13 6n13

题目翻译来自NOCOW。

USACO Training Section 1.5

【算法分析】

与之前的八皇后问题相似,把棋盘从 8 x 8 8x8 8x8大小换成 n x n nxn nxn大小,算法相同,需要注意的是必须修改数组空间大小,适应题目要求的数据范围

【参考程序】

#include<bits/stdc++.h>
using namespace std;
int a[15],b[15],c[30],d[30],tot=0;
void search(int);int n;
int print();
int main(){
	
	cin>>n;
	search(1);
	cout<<tot;
	return 0;
}
void search(int i){
	int j;
	for(j=1;j<=n;j++){
		if((!b[j])&&(!c[i+j])&&(!d[j-i+7])){//!b[j]--->表示不能在同一列
		//!c[i+j]--->表示不能在正对角线上
		//!d[i-j+7]--->表示不能在反对角线上
			a[i]=j;
			b[j]=1;
			c[i+j]=1;
			d[j-i+7]=1;
			if(i==n){
				tot++;
				if(tot<=3){
					for(int x=1;x<=n;x++){//按列输出皇后
						printf("%d ",a[x]);
                    	}printf("\n");
				}
				}
			else search(i+1);
			b[j]=0;
			c[i+j]=0;
			d[j-i+7]=0;
		}
	}
}

P1562 还是 N 皇后

还是 N 皇后

题目描述

正如题目所说,这题是著名的 N N N 皇后问题。

输入格式

第一行有一个 N N N。接下来有 N N N N N N 列描述一个棋盘,* 表示可放,. 表示不可放。

输出格式

输出方案总数。

样例 #1

样例输入 #1

4
**.*
****
****
****

样例输出 #1

1 

提示

0 < n ≤ 14 0< n\le14 0<n14

未待完续

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐