P1220 皇后摆放问题

675 阅读2分钟

image.png

image.png

题目要求:

image.png

首先国际象棋的规则:

image.png

大概意思就是:

如果一个地方有皇后,那么它的横排、纵排、左斜排和右斜排都不能放其他皇后。

运用递归回溯做(图片源自网络,侵删)

第一个皇后:

image.png

(绿的地方不能放皇后)

第二个皇后:

image.png

第三个皇后:

image.png

第四个皇后:

image.png

第五个皇后:

image.png

第六个皇后就没位置了,于是第五个皇后就不能摆放在这里,就要进行回溯

image.png

此时发现,第五个皇后摆在哪里都不能摆下八个皇后

然后就要回溯到第四个皇后:

image.png

然后放第五个皇后......

非递归回溯框架设计

首先我们可以先理解书上的八皇后代码

书(算法设计与分析第二版)P190页

首先有几点说明:

(1)(i,q(i))是皇后的位置,不用0下标,棋盘是从1~8,这时候数组就要定义成q[9];

(2)此题的要求是要找到所有的解;

(3)当第i个皇后可以放的位置都放过时,回溯到第i-1个皇后

(4)顺序是一行一行来,因为一行最多只能放一个皇后TRF

然后定义了两个函数:

dispasolution(int)就是输出八个皇后各在哪儿的函数

void dispasolution(int n) {
	printf("第%d个解:",++count);
	for(int i=1;i<=n;i++){
		printf("(%d,%d)",i,q[i]);
	} 
	printf("\n");
}

然后是place(int i)函数,判断当前的点是不是合法的(横竖左右斜方向有没有皇后)

这个地方很巧妙,首先这里是默认一行只有一个皇后的,所以横排不会非法

然后(q[j] == q[i])判断竖排是否非法,然后(abs(q[j]-q[i])==abs(j-i))判断左右斜排是否合法

bool place(int i) {
  	int j =1;
	if(i==1) 
		return true;
	while(j<i){
		if((q[j] == q[i])||(abs(q[j]-q[i])==abs(j-i)))
			return false;
		j++;
	}
	return true;
}

主函数相信大家都能看得懂。

主要分析Queens()函数:

void Queens(int n){
	int i=1;
	q[i]=0;
	while(i>=1){
		q[i]++;
		while(q[i]<=n&&!place(i))
			q[i]++;
		if(q[i]<=n){
			if(i==n)
				dispasolution(n);
			else{
				i++;
				q[i]=0;
			} 
		} 
		else i--;
	}
}

void Queens(int n){//传入一共几个皇后

int i=1;//i表示当前行也表示第几个皇后

q[i]=0;//第i个皇后的纵坐标从0开始到n一个一个遍历,判断这个位置是否合法

上面说到:当第i个皇后可以放的位置都放过时,回溯到第i-1个皇后。while循环就是这个作用:

  • 如果这个点可以,就i++,考虑下一个皇后;

  • 如果这个点的所有可能都走过了,就i--,考虑上一个皇后

while(i>=1){
                //这一行一个一个点开始遍历
		q[i]++;
                //判断这个点是否合法
		while(q[i]<=n&&!place(i))
                        //如果不合法就继续在这一行往后遍历,找到点或者到棋盘边缘了就跳出while
			q[i]++;
                //判断一下上面的循环是因为到边缘跳出来的,还是找到了合法的点
                //找到了合法的点
		if(q[i]<=n){
                        //如果已经八个皇后了就打印出答案
			if(i==n)
				dispasolution(n);
                        //没到八个皇后的话就下一个皇后     
			else{
				i++;
				q[i]=0;
			} 
		} 
                //这一行没有找到合法的点,返回上一个皇后
		else i--;
	}

运行结果:

image.png 完整代码:

#include<stdio.h>
#include<stdlib.h>

#define MAXN 9

int q[MAXN],count=0;

void dispasolution(int n) {
	printf("第%d个解:",++count);
	for(int i=1;i<=n;i++){
		printf("(%d,%d)",i,q[i]);
	} 
	printf("\n");
}

bool place(int i) {
  	int j =1;
	if(i==1) 
		return true;
	while(j<i){
		if((q[j] == q[i])||(abs(q[j]-q[i])==abs(j-i)))
			return false;
		j++;
	}
	return true;
}
 
void Queens(int n){
	int i=1;
	q[i]=0;
	while(i>=1){
		q[i]++;
		while(q[i]<=n&&!place(i))
			q[i]++;
		if(q[i]<=n){
			if(i==n)
				dispasolution(n);
			else{
				i++;
				q[i]=0;
			} 
		} 
		else i--;
	}
}

int main(){
	int n;
	scanf("%d",&n);
	Queens(n);
	return 0;
}

相信理解了这个问题,上面的题目就好做了很多。

我是用的三维数组回溯做的,需要源码可以私信~