【搜索】还是N皇后

  先看题才是最重要的:  

  

  这道题有点难理解,毕竟Code speaks louder than words,所以先亮代码后说话:

 1 #include<iostream>
 2 using namespace std;
 3 char s[1000];int n,map[1000],mod,ans;
 4 void dfs(int deep,int line,int lr,int rl)
 5 {
 6     if(deep>n)
 7     {
 8         ans++;
 9         return;
10     }
11     int pos=mod&(~(line|lr|rl|map[deep])),p;
12     while(pos)
13     {
14         p=pos&-pos;
15         pos-=p;
16         dfs(deep+1,line+p,(lr+p)<<1,(rl+p)>>1);
17     }
18 }
19 int main()
20 {
21     cin>>n;
22     mod=(1<<n)-1;
23     for(int i=1;i<=n;i++)
24     {
25         cin>>s;
26         for(int j=0;j<n;j++)
27         map[i]=(s[j]==‘.‘)+(map[i]<<1);
28     }
29     dfs(1,0,0,0);
30     cout<<ans;
31     return 0;
32 }

  这道题是一道搜索+二进制优化题,其实是八皇后的升级版,这就说明你的前置要求是要回普通的八皇后(不会点这里),初见此题,小编便鼓起勇气,without thinking twice就稍加改动提交了一遍原来八皇后的代码,结果甚是残忍。

  

  就算是开O2优化也没有用,亲测无效。那么就只能换个思路了,怎样能快一些呢?我们不禁会联想到二进制和位运算(推荐隔壁Alan_Anders的博客二进制和位运算符,小编懒得再多写一篇这样的博客了),我们可以尝试把这个问题简单化,只考虑最根本的问题:我们通常判断一个格子是否可以放置皇后,需要哪些要素呢?这就很显然了,只要懂国际象棋规则的,就一定知道只要这个位置没有被其他皇后攻击到就可以了呗,但是这样判断就很麻烦,比较耗时间,如果我们每一次能失去这个位置能否放置皇后的判断,时间复杂度将会降低不少吧?可这也意味着我们必须达到每一次搜索都能精准的判断出下一次放置的位置在哪里,这便有了五个要素的判断。

  1)这个位置本身题目要求就不能放,这就没办法了,只能用二维数组来存了如果是‘ . ’,那么就存为1,表示不能放,否则为0。1和0你们会想到什么,这就是二进制,那么我们是否可以改存成一维数组呢?举个栗子:

  

  这样我们是不是就可以以十进制存储二进制的方式存下每一行,定义一个数组map,按此图为例,那么map[1]=4,也就是(0010)?,这里默认大家二进制会一些基础知识。

  【前方高能】

  2)在行上的冲突:这就很容易了,每放完一个皇后之后,只要把当前行号+1就可以了,比如现在在第二行放了一个皇后,那么下一次放就会在第三行(也就是2+1=3(行))。

  3)在列上的冲突:众所周知,皇后更攻击的范围包括了它所在的这一列,这样进行操作很简单,比如在第三列放了一个皇后,那么这一列永远也不能放置皇后了。

  那么说了,这么多,用什么来表示行列上的状态呢?行上就不必多说了,直接递归时行数的参数加1就可以了。那么列呢?也要用二进制,举个栗子:

  

  按照上面的图来说:这次我要在红色格子上放一次皇后,那么我下一次放置,哪些地方已经不能放了呢?显然,如下图所示的蓝色格子和第一行(因此表示行号的参数加1)已经不能放了。

  

  那么蓝色格子就会标记为1。下一行的列将会从(0000)?更改成(0100)?。

  4)左上到右下的对角线:我们依旧使用二进制来存储,如下图(还是同样的位置、同样的图):

  

  小编依旧要在红色格子上放置皇后,那么下一行哪些格子会因为左上到右下的对角线而不能放呢?如图所示,蓝色格子就一定不能。

  

  然后标记为1,于是表示左上到右下的对角线二进制值从(0000)?变到了(0010)?。

  4)右上到左下的对角线:同上,只不过会影响到下一行的左边而不是右边,从(0000)?变到了(1000)? 。

  我想这些都应该很好理解吧,这是很简单易懂的,以这个栗子为栗,整理(0010)?、(1000)?、(0100)?,用异或(符号:|)的方法合并成(1110)?,这时我们能轻易的发现下一行的第四位一定是可以放皇后的,因为第四位是0,可是电脑不断找0是浪费时间的,但是找1是却是简单了,现将(1110)?取反变成(0001)?,那么又怎么找1?还记得树状数组中有个lowbit(假设有一个数x,那么x&-x便是lowbit(x)的值)吗?利用lowbit原理(恕小编不才,不会证明,但只要记住这个函数可以取到一个数的二进制数的最右边的1),就可以找到1的位置(也就是取反前0的位置),然后递归这个位置即可,因为可能下一行有>1个位置可以放皇后,所以要减去这个1,再找下一个1,然后继续进行递归。

  【前方高能】

  问题又来了!这个搜索该怎么写?我想你现在一定满肚子问题,现在小编就来回答一下这些问题,看一看与你现在想的一样不一样!

  Q:递归参数怎么写?有哪些要素组成?

  A:递归参数还是比较简单的,只需要4个参数,分别是行、列。左上到右下的对角线和右上到左下的对角线的状态。

  Q:这些状态怎么表示?

  A:全部使用二进制,1表示当前位置不可放,0表示当前位置可以放。

  Q:这些二进制表示的状态是怎么存储的,一直在说是二进制,到底怎么判断?

  A:一直在说二进制是为了好理解,但是我们要存成十进制的数,如(1011)?要存成的数值为13。

  Q:为什么看到亮出的代码上会有mod=(1<<n)-1;和int pos=mod&(~(line|lr|rl|map[deep])),p;呢?mod到底是用来干啥的?

  A:注意:我们的二进制表示数一定只有n位,比如是4*4的棋盘,那么一定二进制表示出的数只有4位,举个栗子:当在第一行第一位放置一个皇后之后,那么下一行右上到左下的二进制表示的数为(10000)?,那么将会多出去一位,为了保持计算的范围不变,那么如果我们用(10000)? &(1111)?,那么结果将会取两个数都是1的位,由于这两个数没有二进制下相同的1的位,所以全部取0,所以下一行右上到左下的二进制表示的数会变成(0000)?。其中mod就是(1111)?,以n=4为例,自己计算一下便会知道mod=(1111)?。

  Q:pos&-pos是什么鬼?

  A:注意看上面的解释,这句话可以当做lowbit(pos),实在不理解就只能去恶补树状数组了。

  Q:实在不理解dfs(deep+1,line+p,(lr+p)<<1,(rl+p)>>1);,肿么办?

  A:那我详细解释一下:deep是行号的意思,下一次放置皇后当然会在下一行喽;那么line呢?这个参数是用来表示列的状态的,比如说原来列的状态是(1001)?(表示1,4列已有皇后放置),现在要在p(0100)?(表示在下一行第二个位置放置皇后) 的位置摆放一个皇后,那么下一次列的状态将会是当前的状态(1001)?+下一次要放皇后的位置(0100)?=(1101)?(从表示1,4列已有皇后放置变成了表示1,2,4列已有皇后放置)喽;斜着的对角线便是一大难点,为什么要左移(右移)?这是因为对角线是斜着的,举个栗子:比如说小编现在这一行表示左上到右下的对角线的二进制表示数为(0010)?,那么这个红色方块内的棋子会通过左上到右下的对角线影响到第三行哪些格子呢?很显然是第三个位置上的棋子,此时第三行的状态将从(0000)?改为(0001)?,这有什么规律呢?当然是第三位的1变成了下一行第四位的1,向右移了1位,因此下一行要向右(向左)移。实在不理解文字描述,可以看下面的flash动画。

  

  //小编自己做的flash动画,如有错误,请大牛指出

  

原文地址:https://www.cnblogs.com/TFLS-gzr/p/10661332.html

时间: 2024-10-25 17:29:52

【搜索】还是N皇后的相关文章

搜索6--noi1700:八皇后问题

搜索6--noi1700:八皇后问题 一.心得 二.题目 1756:八皇后 查看 提交 统计 提问 总时间限制:  1000ms 内存限制:  65536kB 描述 会下国际象棋的人都很清楚:皇后可以在横.竖.斜线上不限步数地吃掉其他棋子.如何将8个皇后放在棋盘上(有8 * 8个方格),使它们谁也不能被吃掉!这就是著名的八皇后问题. 对于某个满足要求的8皇后的摆放方法,定义一个皇后串a与之对应,即a=b1b2...b8,其中bi为相应摆法中第i行皇后所处的列数.已经知道8皇后问题一共有92组解(

搜索5--noi1700:八皇后问题

搜索5--noi1700:八皇后问题 一.心得 二.题目 1700:八皇后问题 查看 提交 统计 提问 总时间限制:  10000ms 内存限制:  65536kB 描述 在国际象棋棋盘上放置八个皇后,要求每两个皇后之间不能直接吃掉对方. 输入 无输入. 输出 按给定顺序和格式输出所有八皇后问题的解(见Sample Output). 样例输入 样例输出 No. 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0

kb-01-a&lt;简单搜索--dfs八皇后问题变种&gt;

题目描述: 在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别.要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案C. Input 输入含有多组测试数据. 每组数据的第一行是两个正整数,n k,用一个空格隔开,表示了将在一个n*n的矩阵内描述棋盘,以及摆放棋子的数目. n <= 8 , k <= n 当为-1 -1时表示输入结束. 随后的n行描述了棋盘的形状:每行有n个字符,其中 # 表示棋盘区域,

棋盘问题 dfs分层搜索(n皇后变式)

棋盘问题 Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 47960   Accepted: 23210 Description 在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别.要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案C. Input 输入含有多组测试数据. 每组数据的第一行是两个正整数,n k,用一个空格隔开,表示

【搜索】八皇后

这道题应该不陌生吧,这是一道很经典的搜索题. 总的意思就是说在一个n*n的棋盘上放n个皇后,要求它们互不攻击,求解有多少种情况,并输出前三种. 那么开始分析:这毕竟是一道搜索题,搜索最大的弊端是什么,当然是时间复杂度极高,虽然这道题可能不会那么卡,我们完全可以开一个二维数组,然后不停标记不能放的位置.但是你是否想过,一维数组+极少的时间复杂度就可以解决问题. 那么具体说一下该怎么放棋子,我们不需要全局的考虑,因为深搜最大的好处是可以简化繁琐的过程,因此我们先考虑只摆一枚棋子,首先假设已经摆好了,

算法学习——DFS(暴力搜索)N皇后问题

N皇后问题是非常经典的一道问题,解题的方法也有很多,非常经典包括暴力回溯法. DFS就是深度优先搜索的首字母,简单理解就是把所有可能是答案的结果都尝试一遍,用走迷宫来举例子的话就是一条路走到黑,如果走到死路了,再退回上一个分岔口选择另一条路继续一条路走到黑. 属于入门时非常常用的暴力算法,考察的知识点主要是对递归的掌握和理解.递归也是新生入门算法时必经的一道门槛,理解透彻递归,就能明白DFS. #include<bits/stdc++.h> using namespace std; const

HDU 搜索练习 N皇后问题

N皇后问题 Time Limit : 2000/1000ms (Java/Other) Memory Limit : 32768/32768K (Java/Other) Total Submission(s) : 39 Accepted Submission(s) : 18 Problem Description 在N*N的方格棋盘放置了N个皇后,使得它们不相互攻击(即任意2个皇后不允许处在同一排,同一列,也不允许处在与棋盘边框成45角的斜线上.<br>你的任务是,对于给定的N,求出有多少种合

[Wikioi 1295]N皇后问题---两种不同的解法(复习)

题目描述 Description 在n×n格的棋盘上放置彼此不受攻击的n个皇后.按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子.n后问题等价于再n×n的棋盘上放置n个皇后,任何2个皇后不妨在同一行或同一列或同一斜线上. 输入描述 Input Description 给定棋盘的大小n (n ≤ 13) 输出描述 Output Description 输出整数表示有多少种放置方法. 样例输入 Sample Input 8 样例输出 Sample Output 92 数据范围

每日一题17:八皇后问题

八皇后问题是由国际西洋棋棋手马克斯·贝瑟尔于1848年提出的:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行.同一列或同一斜线上,问有多少种摆法. 高斯认为有76种方案.1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果.解题的思路如下:从棋盘的第一行起,先选择第一个格子作为第一个皇后的位置,然后在第二行中从第一个格子开始试探一个合适的位置放置第二个皇后,接下来在第三行从第一个格子开始试探一个合适的位置放置第三个皇

回溯法:八皇后问题

八皇后问题是一个以国际象棋为背景的问题:如何能够在 8×8 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行.纵行或斜线上.八皇后问题可以推广为更一般的n皇后摆放问题:这时棋盘的大小变为n×n,而皇后个数也变成n.当且仅当 n = 1 或 n ≥ 4 时问题有解. 这个问题简化描述就是:在8x8的棋盘上放8颗子,要求它们[不在同一行][不在同一列][不在同一斜线]上. 我们可以定义一个数组position[8],positon