八皇后问题

八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯•贝瑟尔于1848年提出:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。如果将旋转和对称的解归为一种的话,则一共有多少个独立解呢?

国际象棋的规则:一个皇后q(x,y)能被满足以下条件的皇后q(row,col)吃掉,即
1)x = row; (在纵向不能有两个皇后)
2) y = col;(横向)
3)col + row = y + x;(斜向正方向)
4) col - row = y - x;(斜向反方向)
遇到上述问题之一的时候,说明我们已经遇到了障碍,不能继续向前了。我们需要退回来,尝试其他路径。
我们可以将棋盘看作是一个8✘8的数组,这样可以使用一种蛮干的思路去解决这个问题,这样我们就是在8✘8=64个格子中取出8个的组合,C(64,80) = 4426165368,显然这个数非常大。
在蛮干的基础上可以增加回溯,可以采用逐步试探的方式,先从一个方向往前走,能进则进,不能进则退,尝试另外的路径。即从第0列开始,我们逐列进行,从第0行到第7行找到一个不受任何已经现有皇后攻击的位置。

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

#define true 1
#define false 0

void solve(int row);
int check(int row, int column);
void output();

int m[8][8] = { 0 }; //表示棋盘,初始为0,表示未放置皇后
int num = 0; //解数目
int from;
int to;
int main() {
	printf("  请输入需要打印的解\n");
	printf("  从from开始to结尾\n");
	scanf("%d",&from);
	scanf("%d",&to);
	solve(1);
	return 0;
}
/*TODO:该函数检查在第row行、第column列放置一枚皇后是否可行
 功能描述:在row行以前的行都放置好皇后的前提下,通过判断纵向,斜向确定当前行列是否可以放置皇后
 参数说明:row-整型 代表棋盘的行号
       column-整型 代表棋盘的列号
 返回值说明:true-可以放置
        false-不可以放置
 异常说明:无
 */
int check(int row, int column) {
	int i, j;

	if (row == 0)
     return true;
		//TODO:第一行随便放 返回true
    for(int i = 0;i < 8;i++) if(i != row && m[i][column])
     return false;
	//TODO:纵向只能有一枚皇后 否则返回false
	int dd = column - row;
    for(int i = 0;i < 8;i++) if(i != row && i + dd >= 0 && i + dd < 8 && m[i][i + dd])
     return false;
	//TODO:左上至右下只能有一枚皇后 否则返回false
    dd = column + row;
    for(int i = 0;i < 8;i++) if(i != row && dd - i >= 0 && dd - i < 8 && m[i][dd - i]) 
    return false;
	//TODO:右上至左下只能有一枚皇后 否则返回false
    return true;
	//TODO:以上以外的时候,检查通过返回true
}

/*TODO:该函数通过递归调用来放置八个皇后
 功能描述:该函数求解当棋盘前row行已放置好皇后,在第row+1行继续放置皇后
 参数说明:row-整型 代表棋盘的行号
 返回值说明:无
 异常说明:无
 */
void solve(int row) {
	int j;
	//考虑在第row行的各列放置皇后
	for (j = 0; j < 8; j++) {
		//TODO:在其中一列放置皇后 注意数组是从0开始计算,第row行,在数组是row-1,数组的j列,对于check函数是j+1
        m[row - 1][j] = 1;
		if (check(row - 1, j)/*TODO:检查在该行该列放置皇后是否可行*/) {

			//若该列可放置皇后,且该列为最后一列,则找到一可行解,输出
			if (row == 8/*TODO:如果当前行为第八行*/) output();
				//TODO:调用输出函数output();
			//若该列可放置皇后,则向下一行,继续搜索、求解
			else solve(row + 1);
				//TODO:递归查找下一行
		}
		//TODO:如果放置不可行,取出该列的皇后,进行回溯即设置为0,继续for循环在其他列放置皇后
		m[row - 1][j] = 0;
	}
}

/*
 功能描述:根据用户输入from和to的值,来输出92个答案中从第from个到第to个答案
 */
void output() {
	int i, j;
	num++;
	if(num>=from && num<=to){
		printf("answer %d:\n", num);
		for (i = 0; i < 8; i++) {
			for (j = 0; j < 8; j++)
				printf("%d ", m[i][j]);
			printf("\n");
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值