八皇后的优化算法-位运算

       原文来自《小华编程网》:八皇后优化-位运算

       这里的两种方法都是递归法,不同的是前者利用了用二位数组表示八皇后的棋盘,后者利用long long 型数据的位。不了解常规方法的请百度,这里详细讲下位运算法。

       以下是位运算法的代码:

//递归法,位运算
//支持n皇后,1<n<sizeof(long long)*4
#include<stdio.h>
#include<time.h>

void queen(long long ld, long long rd);

long long row, sum, uplimit, upperlim;	//row的位表示每一列的状态,为1表示该列放有了皇后。sum表示摆放n皇后的不同摆法。
										//uplimit用来记录
int n;//表示n皇后
void main()
{
	long long ld, rd;	//ld中位为1的表示反斜杠线上不能放皇后的位置,rd的1表示斜杠线上不能放皇后的位置
	row = sum = ld = rd = 0;
	clock_t start_t, finish_t;   //用于计算程序运行的时间
	double user_t;

	printf("输入:");
	scanf_s("%d", &n);
	uplimit = upperlim = (1 << n) - 1;

	start_t = clock();
	queen(ld, rd);
	finish_t = clock();

	printf("求%d皇后的所需时间:%f\n", n, (finish_t - start_t) / (double)CLOCKS_PER_SEC);
	printf("%d\n", sum);
}

void queen(long long ld, long long rd)
{
	long long pos = ~(row | ld | rd);	//算出当前行上那些位置已摆放有皇后了
	rd = rd&upperlim;	//rd向右移位时最高位会填补1,所以为了保证rd左边的sizeof(long long)*8-n位全为0,必须要进行这步运算
						//因为ld向左移位是最低位填补0,ld不需要进行这步运算
	pos = pos&upperlim;	//由于ld左边的sizeof(long long)*8-n有些为1,所以为了保证rd左边的sizeof(long long)*8-n位全为0,必须要进行这步运算
	if (pos != 0)	//判断该行上是不是全部都摆放了皇后
	{
		while (pos != 0)
		{
			long long p = pos&(~pos + 1);	//从当前行可以摆放皇后的位置中选择最右的一位
			p = p&upperlim;
			pos -= p;	//去除上上步操作里选择过的那位
			row = row | p;
			if (uplimit != row)		//判断该行是不是最后一行,若是sum++,不是则进入下一行
			{
				queen((ld | p) << 1, (rd | p) >> 1);
			}
			else
				sum++;
			row = row & ~p;
		}
	}
}
        代 码详解:
                根据矩阵的对角关系,第n行的rd向左移一位,把rd左边的sizeof(long long)*8-n位全置为0( rd = rd&upperlim ),便可以得到下一行由于前n行已经摆放皇后而不能摆放在反斜杠上的皇后的位置。ld同理



          如上图,当前处理第4行,row=101010,表示第1,3,5列都放置了皇后;ld=100100 表示前3行放置的皇后在反斜杠线上影响第4行不能放皇后的位置,即第4行第1,3个位置不能放皇后;rd=000111,同理第4行4,5,6不能放皇后。
           现在第4行可以放的位置为long long pos = ~(row | ld | rd),pos为010000,则只有第2位可以放皇后,此时p=010000 ,row = row | p。
          接下来进入第5行的运算,ld则变为(ld | p) << 1 ,即是ld=101000;rd则变成(rd | p) >> 1,即是rd=001011。
          在进入下一行的运算前进行if (uplimit != row),判断该行是不是最后一行,若是sum++,不是则进入下一行运算。当退出queen()函数之后row = row & ~p,循环选择第4行的位置来摆放皇后,直到pos == 0


         附上常规方法的代码:

//递归法,普通算法
#include<stdio.h>
#include<time.h>

#ifndef LINE
#define LINE 8
#define LIST LINE
#endif

char map[LINE][LIST] = { { '0' } };
//  !表示该行皇后可能的位置    0表示不能站的位置    每一行第一个位置当为#时表示该行还没有计算出该行可以放皇后的位置,当为数值表示当前皇后的位置

int CalcuValue(int line, int list) //return the value pos in this line
{
	for (int line0 = 0; line0 < line; line0++)
	{
		if (map[line0][list] == '1')
			return 0;
	}
	for (int line0 = line - 1, list0 = list - 1; line0 >= 0 && list0 >= 0; line0--, list0--)
	{
		if (map[line0][list0] == '1')
			return 0;
	}
	for (int line0 = line - 1, list0 = list + 1; line0 >= 0 && list0 <= LIST - 1; line0--, list0++)
	{
		if (map[line0][list0] == '1')
			return 0;
	}
	map[line][list] = '1';
	return 1;
}

void reset(int line, int list)
{
	map[line][list] = '0';
	return;
}


int Queen(int line);
int number = 0;

void main()
{
	clock_t start, finish;
	double duration;
	start = clock();
	Queen(0);
	finish = clock();
	duration = (double)(finish - start) / CLOCKS_PER_SEC;
	printf("%ld\ntime:%f", number, duration);
}

int Queen(int line)
{
	for (int index = 0; index <= (LIST - 1); index++)
	{
		if (CalcuValue(line, index))
		{
			if (line == (LINE - 1))
				number++;
			else
				Queen(line + 1);
			reset(line, index);
		}
	}
	return 1;
}

         以下是运行结果比较:


        优化还是相当明显的,因为位操作运算比操作数组运算速度快,通过几个变量来代替一个数组使得运算的内容大大减少。

       代码较简陋,表达不是很到位,如有错误,请指出,共同学习。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值