关于N皇后问题高效试探回溯算法的分析

2023-11-23 18:38

本文主要是介绍关于N皇后问题高效试探回溯算法的分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

// N Queens Problem
// 试探-回溯算法,递归实现

// sum用来记录皇后放置成功的不同布局数;upperlim用来标记所有列都已经放置好了皇后。
long sum = 0, upperlim = 1;     

// 试探算法从最右边的列开始。
void test(long row, long ld, long rd) 。
{
   if (row != upperlim)
   {
     // row,ld,rd进行“或”运算,求得所有可以放置皇后的列,对应位为0,
     // 然后再取反后“与”上全1的数,来求得当前所有可以放置皇后的位置,对应列改为1。
     // 也就是求取当前哪些列可以放置皇后。
     long pos = upperlim & ~(row | ld | rd); 
     while (pos) // 0 -- 皇后没有地方可放,回溯。
     {
        // 拷贝pos最右边为1的bit,其余bit置0。
        // 也就是取得可以放皇后的最右边的列。
        long p = pos & -pos;                                              

        // 将pos最右边为1的bit清零。
        // 也就是为获取下一次的最右可用列使用做准备,
        // 程序将来会回溯到这个位置继续试探。
        pos -= p;                           

        // row + p,将当前列置1,表示记录这次皇后放置的列。
        // (ld + p) << 1,标记当前皇后左边相邻的列不允许下一个皇后放置。
        // (ld + p) >> 1,标记当前皇后右边相邻的列不允许下一个皇后放置。
        // 此处的移位操作实际上是记录对角线上的限制,只是因为问题都化归
        // 到一行网格上来解决,所以表示为列的限制就可以了。显然,随着移位
        // 在每次选择列之前进行,原来N×N网格中某个已放置的皇后针对其对角线
        // 上产生的限制都被记录下来了。
        test(row + p, (ld + p) << 1, (rd + p) >> 1);                              
       }
   }
   else   
   {
       // row的所有位都为1,即找到了一个成功的布局,回溯。
       sum++;
   }
}

int main(int argc, char *argv[])
{
   time_t tm;
   int n = 16;

   if (argc != 1)
   n = atoi(argv[1]);
   tm = time(0);

   // 因为整型数的限制,最大只能32位,
   // 如果想处理N大于32的皇后问题,需要
   // 用bitset数据结构进行存储。
   if ((n < 1) || (n > 32))                 
   {
   printf(" 只能计算1-32之间/n");
   exit(-1);
   }
   printf("%d 皇后/n", n);

   // N个皇后只需N位存储,N列中某列有皇后则对应bit置1。
   upperlim = (upperlim << n) - 1;         

   test(0, 0, 0);
   printf("共有%ld种排列, 计算时间%d秒 /n", sum, (int) (time(0) - tm));
}

上述代码容易看懂,但我觉得核心的是在针对试探-回溯算法所用的数据结构的设计上。
程序采用了递归,也就是借用了编译系统提供的自动回溯功能。

算法的核心:使用bit数组来代替以前由int或者bool数组来存储当前格子被占用或者说可用信息,从这

可以看出N个皇后对应需要N位表示。
巧妙之处在于:以前我们需要在一个N*N正方形的网格中挪动皇后来进行试探回溯,每走一步都要观察

和记录一个格子前后左右对角线上格子的信息;采用bit位进行信息存储的话,就可以只在一行格子也

就是(1行×N列)个格子中进行试探回溯即可,对角线上的限制被化归为列上的限制。
程序中主要需要下面三个bit数组,每位对应网格的一列,在C中就是取一个整形数的某部分连续位即可


row用来记录当前哪些列上的位置不可用,也就是哪些列被皇后占用,对应为1。
ld,rd同样也是记录当前哪些列位置不可用,但是不表示被皇后占用,而是表示会被已有皇后在对角线

上吃掉的位置。这三个位数组进行“或”操作后就是表示当前还有哪些位置可以放置新的皇后,对应0

的位置可放新的皇后。如下图所示的8皇后问题求解得第一步:
row:          [ ][ ][ ][ ][ ][ ][ ][*]
ld:           [ ][ ][ ][ ][ ][ ][*][ ]
rd:           [ ][ ][ ][ ][ ][ ][ ][ ]
--------------------------------------
row|ld|rd:    [ ][ ][ ][ ][ ][ ][*][*]
所有下一个位置的试探过程都是通过位操作来实现的,这是借用了C语言的好处,详见代码注释。

关于此算法,如果考虑N×N棋盘的对称性,对于大N来说仍能较大地提升效率!

这篇关于关于N皇后问题高效试探回溯算法的分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/419936

相关文章

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操

Java中的雪花算法Snowflake解析与实践技巧

《Java中的雪花算法Snowflake解析与实践技巧》本文解析了雪花算法的原理、Java实现及生产实践,涵盖ID结构、位运算技巧、时钟回拨处理、WorkerId分配等关键点,并探讨了百度UidGen... 目录一、雪花算法核心原理1.1 算法起源1.2 ID结构详解1.3 核心特性二、Java实现解析2.

Redis出现中文乱码的问题及解决

《Redis出现中文乱码的问题及解决》:本文主要介绍Redis出现中文乱码的问题及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 问题的产生2China编程. 问题的解决redihttp://www.chinasem.cns数据进制问题的解决中文乱码问题解决总结

MySQL中的表连接原理分析

《MySQL中的表连接原理分析》:本文主要介绍MySQL中的表连接原理分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、环境3、表连接原理【1】驱动表和被驱动表【2】内连接【3】外连接【4编程】嵌套循环连接【5】join buffer4、总结1、背景

全面解析MySQL索引长度限制问题与解决方案

《全面解析MySQL索引长度限制问题与解决方案》MySQL对索引长度设限是为了保持高效的数据检索性能,这个限制不是MySQL的缺陷,而是数据库设计中的权衡结果,下面我们就来看看如何解决这一问题吧... 目录引言:为什么会有索引键长度问题?一、问题根源深度解析mysql索引长度限制原理实际场景示例二、五大解决

Springboot如何正确使用AOP问题

《Springboot如何正确使用AOP问题》:本文主要介绍Springboot如何正确使用AOP问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录​一、AOP概念二、切点表达式​execution表达式案例三、AOP通知四、springboot中使用AOP导出

在Golang中实现定时任务的几种高效方法

《在Golang中实现定时任务的几种高效方法》本文将详细介绍在Golang中实现定时任务的几种高效方法,包括time包中的Ticker和Timer、第三方库cron的使用,以及基于channel和go... 目录背景介绍目的和范围预期读者文档结构概述术语表核心概念与联系故事引入核心概念解释核心概念之间的关系

Python中Tensorflow无法调用GPU问题的解决方法

《Python中Tensorflow无法调用GPU问题的解决方法》文章详解如何解决TensorFlow在Windows无法识别GPU的问题,需降级至2.10版本,安装匹配CUDA11.2和cuDNN... 当用以下代码查看GPU数量时,gpuspython返回的是一个空列表,说明tensorflow没有找到

解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘问题

《解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘问题》:本文主要介绍解决未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4... 目录未解析的依赖项:‘net.sf.json-lib:json-lib:jar:2.4‘打开pom.XM