C/C++面试(6)——N皇后问题

C/C++中位运算的各种技巧 最近在准备面试的过程中,遇到一道这样的面试题,题目描述如下: There are two int variables : a and v, don't use 'if', '?:', 'switch',or other judgement statements, find out the biggest one of two numbers. (美国著名网络开发公司2005年面试题) 相信读者 阅读详情

八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。


常规做法:http://blog.youkuaiyun.com/justme0/article/details/7540425

//  N皇后问题  (递归,回溯)
  
#include <iostream>  
using namespace std;  
  
#define N 8  
  
int matrix[N + 1][N + 1] = {0};  
//  matrix[0][j]为空,matrix[i][0]中放第i行的皇后的列坐标(从1开始记)  
  
bool IsLegal(const int &i, const int &j)  
{  
    //  判断前面的i-1个棋子(坐标是matrix[m][n])与matrix[i][j]是否冲突,i为1时合法  
    for (int m = 1; m <= i - 1; ++m) {  
        int n = matrix[m][0];  
        if (  n == j || abs(i - m) == abs(j - n) )  
            return false;  
    }  
    return true;  
}  
  
void Print(void)  
{  
    static int count = 1;  
    printf("Case %d:\n", count++);  
    for (int i = 1; i <= N; i++) {  
        for (int j = 1; j <= N; j++) {  
            matrix[i][j] == 1 ? printf("%c ", 2) : printf(". ");  
        }  
        cout << endl;  
    }  
    cout << endl;  
}  
  
void Trial(const int &i)  
{  
    //  进入本函数时,在N*N的棋盘前i-1行已放置了互不攻击的i-1个棋子  
    //  现从第i行起继续为后续棋子选择合适位置  
  
    if (i > N)   //  输出当前的合法布局  
        Print();  
    else  
        for (int j = 1; j <= N; ++j) {  
            matrix[i][j] = 1;  
            if ( IsLegal(i, j) ) {  
                matrix[i][0] = j;  
                Trial(i + 1);  
            }  
            matrix[i][j] = 0;  
        }  
}  
  
int main(void)  
{  
    Trial(1);  
  
    return 0;  
}  



目前公认最高效的做法:http://blog.youkuaiyun.com/hackbuteer1/article/details/6657109

使用位运算来求解N皇后的高效算法

   核心代码如下:

[cpp]  view plain copy
  1. void test(int row, int ld, int rd)  
  2. {  
  3.     int pos, p;  
  4.     if ( row != upperlim )  
  5.     {  
  6.         pos = upperlim & (~(row | ld | rd ));  
  7.         while ( pos )  
  8.         {  
  9.             p = pos & (~pos + 1);  
  10.             pos = pos - p;  
  11.             test(row | p, (ld | p) << 1, (rd | p) >> 1);  
  12.         }  
  13.     }  
  14.     else  
  15.         ++Ans;  
  16. }  

        初始化: upperlim =  (1 << n)-1; Ans = 0;

        调用参数:test(0, 0, 0);

         和普通算法一样,这是一个递归函数,程序一行一行地寻找可以放皇后的地方。函数带三个参数row、ld和rd,分别表示在纵列和两个对角线方向的限制条件下这一行的哪些地方不能放。位于该行上的冲突位置就用row、ld和rd中的1来表示。把它们三个并起来,得到该行所有的禁位,取反后就得到所有可以放的位置(用pos来表示)。

        p = pos & (~pos + 1)其结果是取出最右边的那个1。这样,p就表示该行的某个可以放子的位置,把它从pos中移除并递归调用test过程。

        注意递归调用时三个参数的变化,每个参数都加上了一个禁位,但两个对角线方向的禁位对下一行的影响需要平移一位。最后,如果递归到某个时候发现row=upperlim了,说明n个皇后全放进去了,找到的解的个数加一。


注:
        upperlime:=(1 << n)-1 就生成了n个1组成的二进制数。
        这个程序是从上向下搜索的。
        pos & -pos 的意思就是取最右边的 1 再组成二进制数,相当于 pos &(~pos +1),因为取反以后刚好所有数都是相反的(怎么听着像废话),再加 1 ,就是改变最低位,如果低位的几个数都是1,加的这个 1 就会进上去,一直进到 0 ,在做与运算就和原数对应的 1 重合了。举例可以说明:

        原数 0 0 0 0 1 0 0 0    原数 0 1 0 1 0 0 1 1

        取反 1 1 1 1 0 1 1 1    取反 1 0 1 0 1 1 0 0
        加1    1 1 1 1 1 0 0 0    加1  1 0 1 0 1 1 0 1

  与运算    0 0 0 0 1 0 0 0    and  0 0 0 0 0 0 0 1
      其中呢,这个取反再加 1 就是补码,and 运算 与负数,就是按位和补码与运算。
       (ld | p)<< 1 是因为由ld造成的占位在下一行要右移一下;
       (rd | p)>> 1 是因为由rd造成的占位在下一行要左移一下。
        ld rd row 还要和upperlime 与运算 一下,这样做的结果就是从最低位数起取n个数为有效位置,原因是在上一次的运算中ld发生了右移,如果不and的话,就会误把n以外的位置当做有效位。
        pos 已经完成任务了还要减去p 是因为?
        while 循环是因为?
        在进行到某一层的搜索时,pos中存储了所有的可放位置,为了求出所有解,必须遍历所有可放的位置,而每走过一个点必须要删掉它,否则就成死循环啦!

         这个是目前公认N皇后的最高效算法。

完整的代码如下:

[cpp]  view plain copy
  1. /* 
  2. ** 目前最快的N皇后递归解决方法 
  3. ** N Queens Problem 
  4. ** 试探-回溯算法,递归实现 
  5. */  
  6. #include "iostream"  
  7. using namespace std;  
  8. #include "time.h"  
  9.   
  10. // sum用来记录皇后放置成功的不同布局数;upperlim用来标记所有列都已经放置好了皇后。  
  11. long sum = 0, upperlim = 1;       
  12.   
  13. // 试探算法从最右边的列开始。  
  14. void test(long row, long ld, long rd)  
  15. {  
  16.     if (row != upperlim)  
  17.     {  
  18.         // row,ld,rd进行“或”运算,求得所有可以放置皇后的列,对应位为0,  
  19.         // 然后再取反后“与”上全1的数,来求得当前所有可以放置皇后的位置,对应列改为1  
  20.         // 也就是求取当前哪些列可以放置皇后  
  21.         long pos = upperlim & ~(row | ld | rd);   
  22.         while (pos)    // 0 -- 皇后没有地方可放,回溯  
  23.         {  
  24.             // 拷贝pos最右边为1的bit,其余bit置0  
  25.             // 也就是取得可以放皇后的最右边的列  
  26.             long p = pos & -pos;                                                
  27.   
  28.             // 将pos最右边为1的bit清零  
  29.             // 也就是为获取下一次的最右可用列使用做准备,  
  30.             // 程序将来会回溯到这个位置继续试探  
  31.             pos -= p;                             
  32.   
  33.             // row + p,将当前列置1,表示记录这次皇后放置的列。  
  34.             // (ld + p) << 1,标记当前皇后左边相邻的列不允许下一个皇后放置。  
  35.             // (ld + p) >> 1,标记当前皇后右边相邻的列不允许下一个皇后放置。  
  36.             // 此处的移位操作实际上是记录对角线上的限制,只是因为问题都化归  
  37.             // 到一行网格上来解决,所以表示为列的限制就可以了。显然,随着移位  
  38.             // 在每次选择列之前进行,原来N×N网格中某个已放置的皇后针对其对角线  
  39.             // 上产生的限制都被记录下来了  
  40.             test(row + p, (ld + p) << 1, (rd + p) >> 1);                                
  41.         }  
  42.     }  
  43.     else     
  44.     {  
  45.         // row的所有位都为1,即找到了一个成功的布局,回溯  
  46.         sum++;  
  47.     }  
  48. }  
  49.   
  50. int main(int argc, char *argv[])  
  51. {  
  52.     time_t tm;  
  53.     int n = 16;  
  54.   
  55.     if (argc != 1)  
  56.         n = atoi(argv[1]);  
  57.     tm = time(0);  
  58.   
  59.     // 因为整型数的限制,最大只能32位,  
  60.     // 如果想处理N大于32的皇后问题,需要  
  61.     // 用bitset数据结构进行存储  
  62.     if ((n < 1) || (n > 32))                   
  63.     {  
  64.         printf(" 只能计算1-32之间\n");  
  65.         exit(-1);  
  66.     }  
  67.     printf("%d 皇后\n", n);  
  68.   
  69.     // N个皇后只需N位存储,N列中某列有皇后则对应bit置1。  
  70.     upperlim = (upperlim << n) - 1;           
  71.   
  72.     test(0, 0, 0);  
  73.     printf("共有%ld种排列, 计算时间%d秒 \n", sum, (int) (time(0) - tm));  
  74.     system("pause");  
  75.     return 0;  
  76. }  

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

        算法的核心:使用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语言的好处,详见代码注释。


蓝桥杯C/C++VIP试题每日一练之2n皇后问题 蓝桥杯每日一练第二十天2n皇后问题。 阅读详情

相关推荐

C/C++常见面试题(二)

return 0;如上的一个宏定义:切记宏定义的本质只是简单的替换,因此对于宏定义的操作,一般要求加括号,比如上述的宏定义,要计算x的三次方的话,应该写成 VHU(x) (x)*(x)*(x)

发狂的小花的博客 1263

位运算解决八皇后问题

使用位运算来求解N皇后的高效算法    核心代码如下: [cpp] view plaincopy void test(int row, int ld, int rd)   {       int pos, p;       if ( row != upperlim )       {           pos = upperlim &

Jackson Zhang 1万+

米哈游 AI产品经理面试题精选:10道高频考题+答案解析(附PDF)

米哈游是全球知名游戏开发商,以《原神》《崩坏》系列著称,专注二次元文化与AI技术创新。本文整理了10道AI产品经理面试题,涵盖Transformer架构、智能NPC设计、机器学习应用、AIGC产品规划等核心领域。题目既考察技术理解(如RAG原理、多模态模型),又关注产品落地(游戏平衡性优化、伦理安全),同时强调跨团队协作能力。参考答案均结合米哈游业务场景,体现技术驱动与玩家体验并重的产品思维,展现AI在游戏行业的创新应用前景。

ZHHHHH15的博客 679

【计算机算法设计与分析】7-6 皇后控制问题(C++_拉斯维加斯算法(Las Vegas))

问题描述 在一个nxn个方格组成的棋盘上的任一方格中放置一个皇后,该皇后可以控制他所在 的行,列以及对角线上的所有方格。对于给定的自然数n,在nxn个方格组成的棋盘上最少要放置多少个皇后才能控制棋盘上的所有方格,且放置的皇后互不攻击? 设计一个拉斯维加斯算法,对于给定的自然数n计算在nxn个方格组成的棋盘上最少要放置多少个皇后才能控制棋盘上的所有方格,且放置的皇后互不攻击。 思路解析 所谓拉斯维加斯算法即为随机化算法,但是为了保证结果的切实可行,约束随机数必须满足皇后互不攻击的条件,因为并不是每一行都必须有

ccql's Blog 3231

【纯血鸿蒙】安装hdc工具

参考资料:

oh.K 5546

ESP32小车DIY:从零搭建无线遥控智能车(附完整代码与避坑指南)

本文详细介绍了如何从零开始DIY一台基于ESP32的无线遥控智能小车。内容涵盖核心硬件选型、电路设计、低延迟ESP-NOW无线通信协议的配置与实战,以及双电机差速控制算法与PWM调速的实现。文章提供了完整的代码示例和关键的避坑指南,旨在帮助开发者快速构建稳定可靠的智能车平台,并为进一步的功能扩展奠定基础。

gamma的博客 469

洛谷 P1219 八皇后

题目: 一个如下的 6×6的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。 上面的布局可以用序列2\ 4\ 6\ 1\ 3\ 5246135来描述,第ii个数字表示在第ii行的相应位置有一个棋子,如下: 行号1\ 2\ 3\ 4\ 5\ 6123456 列号2\ 4\ 6...

xiaolan7777777的博客 714

华为OD统一考试 - 模拟消息队列(Java & Python& JS & C++ & C )

让我们来模拟一个消息队列的运作,有一个发布者和若干消费者,发布者会在给定的时刻向消息队列发送消息,

算法大师! 3518

【算法问题】N 皇后问题

本文介绍了 N 皇后问题的相关知识。

代码星辰的博客 4973

漫画安全HIDS、EDR、NDR、XDR

漫画安全HIDS、EDR、NDR、XDR

墨痕诉清风的博客 3445

备战2025数学建模国赛(算法46):多目标优化

本篇博客将深入浅出地介绍多目标优化的核心概念、主流算法,并重点结合一个数学建模竞赛的经典案例,提供完整的Python代码实现,助你在未来的竞赛中游刃有余。它通过寻找帕累托最优解集,完美地诠释了“鱼与熊掌不可兼得”的权衡思想,为最终决策提供科学依据。这些问题都包含多个相互冲突的目标,传统的单目标优化方法(如梯度下降、单纯形法)对此无能为力,因为它们无法同时优化多个目标。在数学建模竞赛中,我们面临的绝大多数问题都不是单一的“最好”,而是充满权衡与妥协的“最优”。记住,成功的建模不仅在于解出问题,更在于清晰地。

YOLO 284

ACWing n-皇后问题

n−n−皇后问题是指将nn个皇后放在n×nn×n的国际象棋棋盘上,使得皇后不能相互攻击到,即任意两个皇后都不能处于同一行、同一列或同一斜线上。 现在给定整数nn,请你输出所有的满足条件的棋子摆法。 输入格式 共一行,包含整数nn。 输出格式 每个解决方案占nn行,每行输出一个长度为nn的字符串,用来表示完整的棋盘状态。 其中.表示某一个位置的方格状态为空,Q表示某一个位置的方格上摆着皇后。 每个方案输出完成后,输出一个空行。 注意:行末不能有多余空格。 输出方...

Stillboring的博客 2481

从零开始讲DDR(7)——modelsim独立仿真DDR4

本文主要内容为DDR4 MIG ip核仿真,modelsim独立仿真DDR4。在进行DDR仿真的时候,我们会面临一个以往常规仿真不存在的问题:DDR本身并不是一个我们通过RTL代码设计出来的部件,常规的模块或者IP仿真,都是基于RTL代码就能进行的,但是回顾一下我们DDR的开发流程,调用的IP核也只是MIG ip,其本质是一个DDRC+DDRPYH的组合,并不能模拟任何DDR本身的行为,因此,在进行仿真之前,我们首先要做的就是获取DDR的仿真模型。

apple_53311083的博客 3224

C/C++基础知识点面试题

目录 一、虚函数的数据结构,如何工作? 二、const与define的区别? 三、指针与引用的区别? 四、指针与数据的区别? 五、不用临时变量实现两个变量的交换 七、一个C++源文件从文本到可执行文件经历的过程 八、C++11新特性 九、C++和C的不同 十、malloc的原理 十一、内存泄漏、野指针 十二、static 十三、union和struct 十四、new与ma...

找个特立不独行的名的博客 2万+

构建栅格地图matlab代码

1 使用MATLAB实现地图栅格化 效果图如下所示 图1 ...

qq_59747472的博客 4517

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

C/C++面试感受和经验以及面试题收藏

http://topic.youkuaiyun.com/u/20080924/15/3b00a84e-970f-4dea-92f2-868c5d1ad825.html 前段时间刚参加了n多公司的C/C++软件工程师的面试,有国企,外企,私企(moto,飞思卡尔,港湾,中国卫星XXX(这个牛))等等等等。感受感想颇多,近日终于空闲,在此表述一下。     本人基本条件:3年开发经验,2year+ Wind...

weixin_34380296的博客 2536

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

C/C++面试之算法系列--打印 N*N 螺旋矩阵

  C/C++面试之算法系列--打印 N*N 螺旋矩阵 VIA和EMC都曾经笔过这个试题 输入N, 打印 N*N 矩阵比如 N = 3,打印:1  2  38  9  47  6  5N = 4,打印:1   2   3   412  13  14  511  16  15  610  9   8   7  /*螺旋矩阵*/#

Sailor_forever 3456

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

面试官:小伙子,说说C/C++是如何进行内存管理的?我:……

当面试官问到C/C++动态内存的问题时,该如何做到对答如流呢?

终为nullptr 6294

N皇后问题深度解析:回溯算法原理与Java/C++实现差异

回溯算法是解决约束满足问题的基础范式,其核心在于系统性探索状态空间、动态剪枝与精确回退。N皇后作为经典案例,直观体现递归决策树构建、冲突检测(行列及对角线)与状态管理机制。在工程实践中,该模型广泛应用于配置分配、路径规划与寄存器分配等场景。本文结合Java引用语义与C++值传递特性,深入剖析一维状态数组优化、手动重置vs自动栈回退等关键差异,并覆盖VSCode环境配置验证、性能调优及工业级位运算加速方案,助力开发者从面试题理解升维至系统建模能力。

weixin_34413802的博客 448

c/c++笔试面试题(5)

.实现双向链表删除一个节点P,在节点P后插入一个节点,写出这两个函数。 2.写一个函数,将其中的/t都转换成4个空格。 3.Windows程序的入口是哪里?写出Windows消息机制的流程。 4.如何定义和实现一个类的成员函数为回调函数? 5.C++里面是不是所有的动作都是main()引起的?如果不是,请举例。 6.C++里面如何声明const void f(void)函数为C程序中的库函数? 7

caojunling的专栏 4134

链表面试题总结 C/C++

数据结构和算法,是我们程序设计最重要的两大元素,可以说,我们的编程,都是在选择和设计合适的数据结构来存放数据,然后再用合适的算法来处理这些数据。       在面试中,最经常被提及的就是链表,因为它简单,但又因为需要对指针进行操作,凡是涉及到指针的,都需要我们具有良好的编程基础才能确保代码没有任何错误。       链表是一种动态的数据结构,因为在创建链表时,我们不需要知道链表的长度,

Dev-L的博客 2423

C/C++面试题系列之3:输出1-N个数字

<br /> <br />题目:用C/C++计算1+2+3+……N<br />条件:<br />1.不使用任何循环语句。(for,while,goto等等)<br />2.不使用任何条件语句。(if,?:等等)<br />3.不使用*/等运算,只使用+-运算 <br />4:更不能写1000个printf或是cout。 <br /> <br />一看到这个题,人们会想到递归,但是递归需要结束条件,题目要去不要任何判断条件,所以不行。<br />例一:变相的用1000个printf<br />#include

Harry的专栏 2411
上一篇: C/C++面试(5)——sizeof字节计算
下一篇: Win10安装eclipse+SDK+ADT
猪杂汤饭
博客等级 码龄12年 238粉丝 67原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值