八皇后问题(用了寄存器的思想)

首先简单介绍一下八皇后问题(抄自百度百科 ←_←)。
八皇后问题,是一个古老而著名的问题,是 回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。  高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。计算机发明后,有多种计算机语言可以解决此问题。
如果要尝试用编程来找出这92个解,敲代码之前,要有一个大概的思路。 首先八皇后问题是图像问题,要换一种方式,转换成数字问题。
我先把我写的程序运行结果发上来。


可能读者看到这个会一脸懵逼,一连串的数就是八皇后的解么?我先解释一下,拿NO.1来说,04752613,第一个解,如下图。

绿色部分表示棋子,可以看到任意两个绿色部分不在同一条直线上。每一行都有0~7,从上往下读每一个绿色方块的数字,刚好就是我的程序输出的第一个解04752613。相信看到这里,读者应该知道我是怎么表达八皇后问题的解了。
我的代码加入了嵌入式里边寄存器的思想,效率更加残暴。本来每一行都要有八个变量,一个棋盘64个位置需要8*8的二维数组,每个变量的值0和1表示对应位置上有没有放棋子。现在,我每一行只用一个char型变量,众所周知,char变量是以8位二进制数来存储的。二进制数每一位的取值只能是0或1。没错,我就是用char变量的每一位的值来表示对应位置上有没有放棋子。比如,某一行的变量的值是4,就是某一行第2列放了棋子(因为4换成2进制就是0000 0100,第二位是1)。
什么是寄存器?额,你就理解为一个变量就行了。但是这个变量神通广大,可以表达出多个“开关”的状态。举个例子,char型变量temp的值是7。char是以8位二进制数存储,7转换成二进制就是111,计算机存的是0000 0111,这里一个变量temp就可以说掌握了8个开关的状态,关关关关 关开开开。查对应位就行了。
然后怎么保证摆放棋子不会“犯规”呢?我用了4个寄存器来(4个 int型变量)表示行、列、2条斜线的占用情况。比如说,我在第一行第一列放了一个棋子,那么我就要在四个寄存器上登记,行寄存器第一位“置1”(避免小白误会,这里以及后面的置1都是指赋值为1,),列寄存器第一位置1,斜线1寄存器第(1+1)位置1,斜线2寄存器第(1-1+7)位置1。然后下一次放棋子之前先查一下寄存器,看看即将要放子的位置是否被占用了。比如我放第二行第一列,查到列寄存器第一位被占用了,pass。比如我放第二行第二列,查到斜线2寄存器第(2-2+7)位被占用了,pass。直到4个寄存器都查询到没有被占用就可以摆放棋子了。
其实行寄存器只是摆设,根本用不上,why?因为我是一行一行地找合适位置,找到合适位置了直接跳到下一行,永远不会出现摆到同一行的情况。
斜线寄存器的置位方式怎么那么奇怪,为什么是行坐标+列坐标 ,还有列坐标-行坐标+7?这个要从数学概念开始讲。还记得高中学的一次函数y=kx+b么?以x为横坐标,y为纵坐标,它画出来的图像就是一条直线。在我们这个问题里面,行是x,列是y。当然了,斜线1斜线2的斜率(k值)分别是1和-1。函数模型就简化成y=x+b及y=-x+b;怎么判断两个点在同一斜线上?看完简化后的函数模型,答案已经呼之欲出了,把x换到y那一边,函数就成了b=y-x和b=y+x;对,只要行坐标+列坐标的值相同,或是列坐标-行坐标的值想同,两个点就在同一斜线上。
解释完了,最后,列坐标-行坐标+7的这个+7又是什么鬼?因为列坐标-行坐标不可避免地会有负数的情况,你说万一我要做第6行第1列的寄存器登记时,我总不能让第-5位置1吧,所以+7之后就完全避免了所有负数情况。因为最极端的情况也就是第7行第0列,0-7的值是-7。
还有,寄存器为什么是int型呢?最极端的情况,第7行第七列需要让斜线1寄存器第14位置1,众所周知,int型数据是以16位二进制数存储的。所以,够用。
讲到这里,我说一下怎么令一个变量第n位置1。    
变量 |=(1<<n);
就这么简单。什么?不懂,百度一下‘’ 位运算 ”吧,解释起来挺累人的,超神奇的东西。
讲到这里差不多就讲完了。
再理一下流程,从第0行第0列开始,查寄存器,不犯规,放棋子,寄存器登记一下。下一行,从第0列开始,查寄存器,犯规,pass。下1列,查寄存器,犯规,pass。下一列……如果一整行查过去,统统犯规,那就返回上一行,查一下上一行棋子摆那儿了,挪到下一列试试。挪到一行末尾了还犯规。继续 查一下上一行棋子摆那儿了,挪到下一列试试。
如果8行都摆玩了,那就输出结果。万事大吉了?理论上可是有92种结果呢,输出一种就完了。还是返回最后一行,挪下一列试试吧。
一直到无法执行返回上一行的操作了,就说明92种解都给找出来了!!!
下面的源代码奉上,仅供参考。最后的最后,虽然我不觉得会有人转载我的文章,如果万一,我是说万一,要转载文章,请跟我说一声。
#include<stdio.h>
#include<stdlib.h>
int temp=0;

/*程序说明:reg[0]用来检测是否在同一行 reg[1]是否同一列  reg[2]、reg[3]是否在同一条斜线上*/
/* i表示列 line表示行 特别说明:假设有两个点的(i+line)或者(i-line)相同  说明它们在同一斜线上 读者可自行验证 */
/* 由于reg是无符号长整型 长度达16位 横纵坐标最小0+0=0 最大7+7=14 占用不过15位 */
/* 每一行的前进用嵌套 每一列的前进用循环*/
void box(char *a,int line,int *reg)//a是一个数组 用来保存每一行的列数  line为行数  reg为检测寄存器
{
	int i,j;
	
	for(i=0;i<8;i++)//从第1列开始分析  到第7列结束
	{
		if( ((reg[1]>>i)&0x01) || ((reg[2]>>(line+i))&0x01) || ((reg[3]>>(7+line-i))&0x01))//任意一位是1表示该行列组合不可用
		{
			;
		}
		else//反之 说明可以用
		{
			a[line]=i;//记录列数
			reg[1] |= (0x01<<i);//寄存器对应位置1
			reg[2] |= (0x01<<(line+i));//寄存器对应位置1
			reg[3] |= (0x01<<(7+line-i));//寄存器对应位置1
			
			
			if(line>=7)  //说明8行都已经摆放好皇后  打印结果
			{
				temp++;
				printf("NO.%d :  ",temp);
				for(j=0;j<8;j++)
				{
					printf("%d ",a[j]);
				}
				printf("\n");
			}
			else//说明还有下一行可以放皇后  递归调用此函数 即进入下一行
				box(a,line+1,reg);
			a[line]=-1;//为了尝试不同的摆法 拿掉皇后 尝试下一个位置
			reg[1] &= ~(0x01<<i);//当然 对应位的值需要清0
			reg[2] &= ~(0x01<<(line+i));//当然 对应位的值需要清0
			reg[3] &= ~(0x01<<(7+line-i));//当然 对应位的值需要清0
		}
	}
	
	return ;
}

int main()
{
	char a[8]={0};//清空棋盘
	int line=0;//初始化行数
	int reg[4]={0};//初始化寄存器
	box(a,line,reg);
	getchar();
	return 0;
}





  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值