题目链接~~>

这题用 dfs()N范围一大了过不了,需要打表,用状态压缩可以状态压缩真是太强大了。

状态压缩 1:

在状态压缩中,通常用 ( 1 << N ) - 1 来表示最大状态MAXST,用 A | B 来合并A和B的状态位,用 A & ~B 来清除A状态中所有的B中的状态位。

一、DFS函数参数Col,MainDiag,ContDiag的表示意义:

当整形数Col,MainDiag,ContDiag的第X位为1时,表示因为之前摆放的皇后的纵向攻击,主对角线斜向攻击,副对角线斜向攻击而使得当前行摆放的皇后不能位于第X列。

二、Col == ( 1 << N ) - 1 的表示意义:

( 1 << N ) - 1 在二进制下就是N个1。这里Col中的1表示的是因为之前摆放的皇后的纵向攻击而使得当前行摆放的皇后不能位于的列,事实上也就是表示已经摆放了皇后的列。那么 Col == ( 1 << N ) - 1 也就表示N列每一列都摆放了皇后,也就是已经摆放了N个皇后,此时即获得一种皇后摆放方案。

三、emptycol = ( ( 1 << N ) - 1 ) & ~( Col | MainDiag | ContDiag ) 的表示意义:

首先 Col | MainDiag | ContDiag 的第X位为1时,显然是表示在综合了之前摆放的皇后的所有攻击方式之后当前行摆放的皇后不能位于第X列。

那么 emptycol = ( ( 1 << N ) - 1 ) & ~( Col | MainDiag | ContDiag ) 的第X位为1时,则是表示当前行摆放的皇后可以位于第X列。

四、curcol = ( emptycol ) & ( ( ~emptycol ) + 1 ) 的表示意义:

emptycol & ~emptycol 的结果显然是0,但是~emptycol一个巧妙的+1之后却让结果极富意义。

首先,2进制数字的+1进位(类比一下10进制)还是不难理解的:从最后一位开始,若当前位数字为1则将其进位为0并继续向前进位,若当前位数字为0则将其进位为1并停止进位。

现假设emptycol中的第一个1在第First位,而其后First-1位都是0。

那么~emptycol中的第一个0在第First位,而其后First-1位都是1。

当~emptycol进行+1进位时,其后First-1位全部进位为0,其第First进位为1,而其First之前的所有位则不变。

这时候再将emptycol与其进行&运算,显然答案的后First-1位为0,第First位为1,而First之前的所有位为0。

也就是说,curcol = ( emptycol ) & ( ( ~emptycol ) + 1 ) 中仅有一个1,而且这个1的位置即为emptycol中最后一个1的位置!

那么curcol的表示意义即为当前行摆放的皇后可以摆放的最后一列。

五、 emptycol &= ~curcol 的表示意义:

清除emptycol的最后一个1,表示当前已将皇后摆放在可以摆放的最后一列,之后不必再对此情况进行DFS。

因而while的循环结束条件是emptycol为0,也就是emptycol中没有1,表示已经没有需要DFS的情况了。

六、DFS递归的函数参数 Col | curcol,( MainDiag | curcol ) >> 1,( ContDiag | curcol ) << 1 的表示意义:

1、Col中的1表示因为之前摆放的皇后的纵向攻击而使得当前行摆放的皇后不能位于的列,而下一行的皇后显然也是不能摆放在这些列的curcol中唯一的1表示当前行皇后摆放的列,那么下一行的皇后因为当前行皇后的纵向攻击而不能摆放在这一列。所以 Col | curcol 即下一行的皇后因为其之前摆放的皇后的纵向攻击而不能摆放的列。

2、MainDiag中的1表示因为之前摆放的皇后的主对角线斜向攻击而使得当前行摆放的皇后不能位于的列。由于攻击是沿主对角线的,故而当前行影响的是第X列的话,那下一行影响的就是第X+1列。因此之前摆放的皇后的主对角线斜向攻击传递到下一行时则将变为 MainDiag >> 1。而因为当前行摆放状态为curcol的皇后的主对角线斜向攻击,下一行摆放的皇后将不能摆放在 curcol >> 1
中唯一的1的所在位置对应的列。所以 MainDiag >>1 | curcol >> 1 也就是 ( MainDiag | curcol ) >> 1 即下一行的皇后因为其之前摆放的皇后的主对角线斜向攻击而不能摆放的列。

3、( ContDiag | curcol ) << 1 的表示意义可类比第2点。

#include<stdio.h>
int N,SchemeNum;
void DFS(int Col,int MainDiag,int ContDiag)// Col 列放置皇后的情况,MainDiag 主对角线放置皇后的情况
{ // ContDiag 次对角线放置皇后的情况
if(Col==(1<<N)-1) // 找到一种放置方案
{
++SchemeNum;
return;
}
int emptycol=(((1<<N)-1)&~(Col|MainDiag|ContDiag)); //当前要放的行可以放的位置
while(emptycol)
{
int curcol=(emptycol)&((~emptycol)+1); //取第一位不为零的位
emptycol&=~curcol; //删除上一次放置的列
DFS(Col|curcol,(MainDiag|curcol)>>1,(ContDiag|curcol)<<1);// (ContDiag|curcol)<<1 最好&(1<<N)-1一下防止溢出
}
}
int main()
{
while(scanf("%d",&N)==1&&N)
{
SchemeNum=0;
DFS(0,0,0);
printf("%d\n",SchemeNum);
}
return 0;
}

状态压缩(2):

      用Col,First和Second分别表示标记了列,主对角线和副对角线的值,初始时全为0,这样的话(Col | First | Second)就表示到当前列时已经标记了的(不能放皇后的)点,那我们对其取反~操作,

也就是CanPut = ~(Col| First | Second),那得到的数就表示可以放的位置,所有1所在的位置也就是可以放的位置,那我们每次取出最低位的1(x & (-x))(这个我之前也不知道,网上查到的),那这样的话就得出了当前所有可以放的位置(不用像上面一个一个判断)(如果可以的话,尽可能自己推出递推关系,这里并不难)。

这里要注意的一个问题就是取反(~)操作之后,由于它高于N的位置也被置为了1,也就是相当于放到了某一行的棋盘的外面去了,所以我设置了一个值High = (1<<N)-1,然后每次得到一个可以放的位置的值CanPut,就对其和High取与(&)运算,这样的话就可以去掉高位的1,得到的数都是High以内的。

然后就是要处理已经处理了此行如何进行递归倒下一行的问题,假设在本行取出了一个CanPut的最低位LowBit,那么到下一行时,下一行的Col就是Col | LowBit, 当前行的First倒下一行时由于所有标记的点都往右移了一格,所以First>>1,再加上LowBit的右边一格也不能放,所以下一行主对角线已经标记了的就是(First>>1 | LowBit>>1),同理,副对角线标记了的就是(Second<<1 | LOwBit <<1).

代码:

#include <stdio.h>
int N,ans,High;
void DFS(int Col,int Fir,int Sec)
{
if(Col == High){ans++;return;}//所有的列都已经被标记了,说明每一列都放了
int CanPut = ((~(Col | Fir | Sec)) & High);
while(CanPut)
{
int LowBit = CanPut & (-CanPut);//取出最低位
DFS((Col|LowBit), ((Fir|LowBit)>>1), (((Sec|LowBit) <<1) & High));
CanPut &= (~LowBit);//去掉最低位
}
} int main()
{
while(~scanf("%d", &N) && N)
{
High = (1<<N)-1; ans = 0;
DFS(0,0,0);
printf("%d\n", ans);
}
return 0;
}

N皇后问题(状态压缩实现)的更多相关文章

  1. HDU2553N皇后问题(状态压缩)

    这道题其实最简单的方法就是打表,直接DFS会超时,那就先运行一遍,找出1~10的值,打表即可,这里提供DFS和打表的数据 DFS:(白书上的)TLE #include <stdio.h> ...

  2. 转 状态压缩DP

    引入 首先来说说“状态压缩动态规划”这个名称,顾名思义,状态压缩动态规划这个算法包括两个特点,第一是“状态压缩”,第二是“动态规划”. 状态压缩: 从状态压缩的特点来看,这个算法适用的题目符合以下的条 ...

  3. HDU-4529 郑厂长系列故事——N骑士问题 状态压缩DP

    题意:给定一个合法的八皇后棋盘,现在给定1-10个骑士,问这些骑士不能够相互攻击的拜访方式有多少种. 分析:一开始想着搜索写,发现该题和八皇后不同,八皇后每一行只能够摆放一个棋子,因此搜索收敛的很快, ...

  4. dp乱写1:状态压缩dp(状压dp)炮兵阵地

    https://www.luogu.org/problem/show?pid=2704 题意: 炮兵在地图上的摆放位子只能在平地('P') 炮兵可以攻击上下左右各两格的格子: 而高原('H')上炮兵能 ...

  5. HDU 2553 状态压缩

    N皇后问题 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submi ...

  6. POJ 3254. Corn Fields 状态压缩DP (入门级)

    Corn Fields Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 9806   Accepted: 5185 Descr ...

  7. HDU 3605:Escape(最大流+状态压缩)

    http://acm.hdu.edu.cn/showproblem.php?pid=3605 题意:有n个人要去到m个星球上,这n个人每个人对m个星球有一个选择,即愿不愿意去,"Y" ...

  8. [HDU 4336] Card Collector (状态压缩概率dp)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4336 题目大意:有n种卡片,需要吃零食收集,打开零食,出现第i种卡片的概率是p[i],也有可能不出现卡 ...

  9. HDU 4336 Card Collector (期望DP+状态压缩 或者 状态压缩+容斥)

    题意:有N(1<=N<=20)张卡片,每包中含有这些卡片的概率,每包至多一张卡片,可能没有卡片.求需要买多少包才能拿到所以的N张卡片,求次数的期望. 析:期望DP,是很容易看出来的,然后由 ...

随机推荐

  1. 微信小程序开发 如何退出当前页面

    默认是在首页 wx.navigateBack({     delta: -1 });     详情参考. https://mp.weixin.qq.com/debug/wxadoc/dev/api/u ...

  2. java.lang.NoClassDefFoundError: javax/servlet/AsyncListener解决方案

    问题:spring3.2的架构在tomcat6.0中无法正常启动,抛出java.lang.NoClassDefFoundError: javax/servlet/AsyncListener错误 原因: ...

  3. openmp查看最大线程数量

    CMakeLists.txt cmake_minimum_required(VERSION 2.8) project(omp_test) find_package(OpenMP REQUIRED) i ...

  4. (转载)dotnet core 中文乱码 codepages

    引子 转载自:http://www.jianshu.com/p/1c9c59c5749a 参考:.Net Core 控制台输出中文乱码 上文中我查阅了一些cli的源码, 闲来无事就继续翻代码, 冥冥之 ...

  5. CND网站加速

    CDN是什么 1-CDN俗称网站加速2-公司一般是购买其他cdn服务商提供的服务3-CDN一般是用来缓存网站的静态资源文件的(css,js,图片,html,htm),浏览器获取某个静态资源是按照就近原 ...

  6. html知识杂记

    1.HTML中不支持 空格.回车.制表符,它们都会被解析成一个空白字符.2.HTML 是用来描述网页的一种语言.3.元素的内容是开始标签与结束标签之间的内容.4.即使 <br> 在所有浏览 ...

  7. [转]docker安装elk

    来源 https://www.jianshu.com/p/f549017c4b60   问题:virtual memory areas vm.max_map_count [65530] likely ...

  8. (二)Makefile——自动编译、清理、安装软件

    每次都要敲击冗长的编译命令,每次都要清理之前的编译中间结果和最终结果,在安装软件时复制剪切大量的动态库,在卸载软件时删除很多的动态库和配置文件.好吧,我被逼向了makefile. helloworld ...

  9. android测试--常用控件测试及测试经验(常见)

    1.图片选择器 ================测试中遇到的问题记录(除表中记录的)================================================== ①.曾出现,断 ...

  10. oracle 中可以用 case when then else end来处理除数是0的情况

    case when a.ZJXJE != 0 then to_char(round((a.YFZK-b.YFZK)/a.ZJXJE,2)) else '本期总进项金额为零' end then和else ...