一、问题描述

在n×n格的国际象棋上摆放n个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。

二、算法设计

解n后问题的回溯算法描述如下:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
int n;
long long int sum;
int x[11];
int Check(int row, int col)
{
for(int i = 1; i < row; i++)
{
if(col == x[i] || abs(row - i) ==abs(col - x[i])) //在同一列或者在同一斜线。一定不在同一行
return 0;
}
return 1;
} void backtrack(int k)
{
if(k>n) //求出一种解, sum+1
{
sum++;
return;
}
for(int i=1; i<=n; i++)//n叉树
{
if(Check(k, i)) //剪枝,检查是否满足条件
{
x[k]=i; //记录第k皇后在第i列
backtrack(k+1); //递归查找
}
} }
int main()
{
while(scanf("%d",&n)!=EOF)
{
if(n==0)
{
break;
}
for(int i=0; i<n; i++)
{
x[i]=0;
}
sum=0;
backtrack(1);
printf("%lld\n",sum);
}
return 0;
}

三、位运算优化

上面的程序我在求16皇后的时候大概跑了近乎200s,我们可以想象到每次搜索第k行的状态的时候,都是从第1列开始枚举每一列,这样是很低效的,浪费了很多时间,我们需要提高枚举的命中率甚至每一次的尝试都是正确的,都是可行解。

那么该怎么做?

其实n皇后的搜索规模并不是很大,在目前的需求中,最多不过20位,我们可以使用二进制来表示一个集合,而一旦使用二进制时,集合的交并补运算就可以直接使用位运算来实现了,我们知道位运算在计算机中是相当快的(使用指令少)。安利一篇我之前做过的位运算的实验https://www.cnblogs.com/wkfvawl/p/10034576.html

两个数字与运算就是求交集,或运算就是求并集,取反就是求集合的补集。

我们先来看程序代码:

void test(int row, int ld, int rd)
{
int pos, p;
if ( row != upperlim )
{
pos = upperlim & (~(row | ld | rd ));
while ( pos )
{
p = pos & (~pos + );
pos = pos - p;
test(row | p, (ld | p) << , (rd | p) >> );
}
}
else
++Ans;
}

初始化:

upperlim =  ( << n)-; Ans = ;

upperlime =(1 << n)-1 就生成了n个1组成的二进制数。

程序从上到下搜索。

这样我们使用三个参数row、ld和rd,分别表示在纵列和两个对角线方向的限制条件下这一行的哪些地方不能放。位于该行上的不能放置的位置就用row、ld和rd中的1来表示。把它们三个并起来,得到该行所有的禁位,取反后就得到所有可以放的位置(用pos来表示)

这里需要注意一点:

对应row、ld和rd来说1表示的是不能放置皇后的占用位置,但对于pos来说1代表可以放置皇后的位置!

 p = pos & (~pos + 1)其结果是取出最右边的那个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

从集合的角度来看p是位置集合pos上的一位置,将皇后置于位置p,位置集合就要减少一个位置,所以需要:

pos = pos - p

那这个while我们也就明白了,需要把位置集合全都用完放置皇后嘛!

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

这里拿两个例子来说明,对于第一张图的例子。

在已经安置好3个皇后的情况下,对于第4个皇后

row = 101010 棕色线代表纵列上不能放置皇后的占位

ld  = 100100  蓝色线代表左对角线列上不能放置皇后的占位

rd =  000111  绿色线代表右对角线列上不能放置皇后的占位

对角线是45度倾斜的,这样两个对角线方向的占位要影响下一行对应位置的下一位也就很好理解了,这恰恰可以使用位运算的左移和右移来实现。

(ld | p)<< 1 是因为由ld造成的占位在下一行要右移一下;

(rd | p)>> 1 是因为由rd造成的占位在下一行要左移一下。

当然 ld rd row 还要和upperlime 与运算 一下,这样做的结果就是从最低位数起取n个数为有效位置,原因是在上一次的运算中ld发生了右移,如果不and的话,就会误把n以外的位置当做有效位。

#include<cstdio>
#include<algorithm>
#define ll long long int
using namespace std; // sum用来记录皇后放置成功的不同布局数;upperlim用来标记所有列都已经放置好了皇后。
ll sum;
ll upperlim = ; // 试探算法从最右边的列开始。
void test(ll row, ll ld, ll rd)
{
if (row != upperlim)
{
// row,ld,rd进行“或”运算,求得所有可以放置皇后的列,对应位为0,
// 然后再取反后“与”上全1的数,来求得当前所有可以放置皇后的位置,对应列改为1
// 也就是求取当前哪些列可以放置皇后
ll pos = upperlim & ~(row | ld | rd);
while (pos) // 0 -- 皇后没有地方可放,回溯
{
// 拷贝pos最右边为1的bit,其余bit置0
// 也就是取得可以放皇后的最右边的列
ll p = pos&-pos; // 将pos最右边为1的bit清零
// 也就是为获取下一次的最右可用列使用做准备,
// 程序将来会回溯到这个位置继续试探
pos -= p;
// row + p,将当前列置1,表示记录这次皇后放置的列。
// (ld + p) << 1,标记当前皇后左边相邻的列不允许下一个皇后放置。
// (ld + p) >> 1,标记当前皇后右边相邻的列不允许下一个皇后放置。
// 此处的移位操作实际上是记录对角线上的限制,只是因为问题都化归
// 到一行网格上来解决,所以表示为列的限制就可以了。显然,随着移位
// 在每次选择列之前进行,原来N×N网格中某个已放置的皇后针对其对角线
// 上产生的限制都被记录下来了
test(row + p, (ld + p) << , (rd + p) >> );
}
}
else
{
// row的所有位都为1,即找到了一个成功的布局,回溯
sum++;
}
} int main()
{
int n;
while(scanf("%d",&n)!=EOF)
{
if(n==)
{
break;
}
sum = ;
upperlim = ( << n) - ;
test(,,);
printf("%lld\n",sum);
}
return ;
}

算法设计与分析——n后问题(回溯法+位运算)的更多相关文章

  1. 算法设计与分析 - AC 题目 - 第 5 弹(重复第 2 弹)

    PTA-算法设计与分析-AC原题 - 最大子列和问题 (20分) 给定K个整数组成的序列{ N1, N2, ..., NK },“连续子列”被定义为{ Ni, Ni+, ..., Nj },其中 ≤i ...

  2. 算法设计与分析 - AC 题目 - 第 2 弹

    PTA-算法设计与分析-AC原题7-1 最大子列和问题 (20分)给定K个整数组成的序列{ N1, N2, ..., NK },“连续子列”被定义为{ Ni, Ni+1, ..., Nj },其中 1 ...

  3. 【技术文档】《算法设计与分析导论》R.C.T.Lee等·第7章 动态规划

    由于种种原因(看这一章间隔的时间太长,弄不清动态规划.分治.递归是什么关系),导致这章内容看了三遍才基本看懂动态规划是什么.动态规划适合解决可分阶段的组合优化问题,但它又不同于贪心算法,动态规划所解决 ...

  4. 算法设计与分析-Week12

    题目描述 You are given coins of different denominations and a total amount of money amount. Write a func ...

  5. 『嗨威说』算法设计与分析 - 回溯法思想小结(USACO-cha1-sec1.5 Checker Challenge 八皇后升级版)

    本文索引目录: 一.回溯算法的基本思想以及个人理解 二.“子集和”问题的解空间结构和约束函数 三.一道经典回溯法题点拨升华回溯法思想 四.结对编程情况 一.回溯算法的基本思想以及个人理解: 1.1 基 ...

  6. 算法设计与分析 - 李春葆 - 第二版 - html v2

    1 .1 第 1 章─概论   1.1.1 练习题   1 . 下列关于算法的说法中正确的有( ).   Ⅰ Ⅱ Ⅲ Ⅳ .求解某一类问题的算法是唯一的   .算法必须在有限步操作之后停止   .算法 ...

  7. 南大算法设计与分析课程复习笔记(1) L1 - Model of computation

    一.计算模型 1.1 定义: 我们在思考和处理算法的时候是机器无关.实现语言无关的.所有的算法运行在一种“抽象的机器”之上,这就是计算模型. 1.2 种类 图灵机是最有名的计算模型,本课使用更简单更合 ...

  8. 算法设计与分析基础 (Anany Levitin 著)

    第1章 绪论 1.1 什么是算法 1.2 算法问题求解基础 1.2.1 理解问题 1.2.2 了解计算设备的性能 1.2.3 在精确解法和近似解法之间做出选择 1.2.4 算法的设计技术 1.2.5 ...

  9. 算法设计与分析(李春保)练习题答案v1

    1.1第1 章─概论 1.1.1练习题 1.下列关于算法的说法中正确的有(). Ⅰ.求解某一类问题的算法是唯一的 Ⅱ.算法必须在有限步操作之后停止 Ⅲ.算法的每一步操作必须是明确的,不能有歧义或含义模 ...

随机推荐

  1. Vue引入

    Vue引入 概念: 1.el:实例 new Vue({ el: '#app' }) // 实例与页面挂载点一一对应 // 一个页面中可以出现多个实例对应多个挂载点 // 实例只操作挂载点内部内容 2. ...

  2. js表达式和语句趣味题讲解与技术分享

    技术分享 问题1 { a: 1 } + 1 // ? ({ a: 1 }) + 1 // ? 1 + { a: 1 } // ? 答案 { a: 1 } + 1 // 1 ({ a: 1 }) + 1 ...

  3. 关于c++函数里面return的用法,关于调用的讲解

    与下面的图片对比一下 可以看见在int b = test();d的时候cout<<"hello";就被调用了: cout<<b;只是返回return a的值 ...

  4. 谈一谈个人利用Java的mysql的知识完成的数据库的项目-----用户信息管理系统

    首先,我先简述一下自己做的过程啊,相信大家来找这样的博客,也都是为了完成自己课程任务吧.我也一样是一名大一的学生,是为了自己的课程任务而开始做数据库的项目的.因为还没学mysql吗,所以是自己找视频啊 ...

  5. Hudi基本概念

    Apache Hudi(发音为"Hoodie")在DFS的数据集上提供以下流原语 插入更新 (如何改变数据集?) 增量拉取 (如何获取变更的数据?) 在本节中,我们将讨论重要的概念 ...

  6. 链接脚本(Linker Script)用法解析(一) 关键字SECTIONS与MEMORY

    1.MEMORY关键字用于描述一个MCU ROM和RAM的内存地址分布(Memory Map),MEMORY中所做的内存描述主要用于SECTIONS中LMA和VMA的定义. 2.SECTIONS关键字 ...

  7. jeecg培训第一课(代码生成与权限分配)

    问题描述:进口部要完成一票进口报关单的增删改查,操作员张三登录只能增删改张三的报关单,操作员李四登录只能增删改李四的报关单, 部门主管王五登录能查看张三和李四的报关单,但不能修改删除.操作员能提交报关 ...

  8. Chrome快捷键吐血整理

    Chrom是平时开发过程中最常用到的浏览器,使用快捷键操作Chrome能提高我们的使用效率,而且可以脱离鼠标进行操作.本篇博客就对平时我们常用的Chrome快捷键做一个整理总结.大家拿走不谢,哈哈~~ ...

  9. react-native scrollview触摸滚动事件

    目录 1.几个已知的滑动或者滑动开始结束的方法: 2.还有其他的一些事件如下 3.下面就这些方法的顺序做个简单的介绍: 4.android上的时间分为两种,一个是滑动一次,一个是连续滑动两次甚至多次, ...

  10. Pandas学习(二)——双色球开奖数据分析

    学习笔记汇总 Pandas学习(一)–数据的导入 pandas学习(二)–双色球数据分析 pandas学习(三)–NAB球员薪资分析 pandas学习(四)–数据的归一化 pandas学习(五)–pa ...