皇后问题(DFS)(位运算)

皇后问题

题目描述:

有一个n*n的棋盘,你要在这个棋盘上放上n个皇后,使得她们不能相互攻击。当然为了使得问题更加有趣,我们在棋盘上限定了一些位置使得这些位置上不能放皇后。

输入格式:

第一行两个数n和m ,表示在一个n*n的棋盘上放n个皇后,有m个受限位置。

接下来m行每行两个数,x和y,表示第x行,第y列这个位置不可以放皇后。

输出格式:

一行一个数 ans,表示总的方案数。

样例输入:

4 1
1 2

样例输出:

1

朴素算法(60分)
#include<iostream>
using namespace std;
int n,m,b[20][20],l[20],pi[40],na[40],x,y,ans;
int DFS(int i){
    if(i==n+1){ans++;return 0;}
    for(int j=1;j<=n;j++){
        if(b[i][j]==0 && l[j]==0 && pi[i+j]==0 && na[i-j+n]==0){
            l[j]=1;
            pi[i+j]=1;
            na[i-j+n]=1;
            DFS(i+1);
            l[j]=0;
            pi[i+j]=0;
            na[i-j+n]=0;
        }
    }
    return 0;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        cin>>x>>y;
        b[x][y]=1;
    }
    DFS(1);
    cout<<ans<<endl;
}

高级算法(位运算)(100分)

#include<iostream>
using namespace std;
int n,m,x,y,ans,a[20];
void DFS(int row,int ld,int rd,int h)
{
    if(row!=(1 << n) - 1){
        int pos=((1<<n)-1)&~(row|ld|rd|a[h]);
        while(pos){
            int p=pos & -pos;
            pos-=p;
            DFS(row+p,(ld+p)<<1,(rd+p)>>1,h+1);
        }
    }
    else ans++;
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        cin>>x>>y;
        a[x]+=1<<(y-1);
    }
    DFS(0,0,0,1);
    cout<<ans<<endl;
}

高级算法(位运算)思路:

初始化: 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以外的位置当做有效位。

时间: 2024-11-09 10:20:52

皇后问题(DFS)(位运算)的相关文章

UVA11127- Triple-Free Binary Strings(DFS+位运算)

题目链接 题意:给出长度为n的字符串,字符串由'1','0','*'组成,其中'*'可以任意替换为'1','0',求不存在连续3个相同子串的字符串的最多个数. 思路:我们可以利用二进制的形式来表示字符串,进行DFS.利用位运算的左移来表示在'*'位置上放置'1',注意在递归的过程中注意判断之否存在3个连续相同的子串. 代码: #include <iostream> #include <cstdio> #include <cstring> #include <alg

UVa 818Cutting Chains (暴力dfs+位运算+二进制法)

题意:有 n 个圆环,其中有一些已经扣在一起了,现在要打开尽量少的环,使所有的环可以组成一条链. 析:刚开始看的时候,确实是不会啊....现在有点思路,但是还是差一点,方法也不够好,最后还是参考了网上的题解,大神们的代码就是不一样, 但还是看了好久才看懂.首先是用二进制法进行暴力,因为 n 最大才是15,不会超时的,然后就是在暴力时判断打开这些环时,剩下的是不是还存在环, 如果存在那么不是不行的,然后再判断是不是有的环有两个分支以上,因为一个环如果成链那么最多只有两个分支,所以多于两个的也是不对

N皇后问题 --使用位运算解决

关键位运算 x & (-x) 取得最低位1 x & (x-1) 去掉最低位1 class Solution(object): def totalNQueens(self, n): """ :type n: int :rtype: int """ if n < 1 : return [] self.count = 0 self.DFS(n,0,0,0,0) return self.count def DFS(self,n, r

位运算总结&amp;拾遗

JavaScript 位运算总结&拾遗 最近补充了一些位运算的知识,深感位运算的博大精深,此文作为这个系列的总结篇,在此回顾下所学的位运算知识和应用,同时也补充下前文中没有提到的一些位运算知识. 把一个数变为大于等于该数的最小的2的幂 一个数为2的幂,那么该数的二进制码只有最高位是1. 根据这个性质,我们来举个栗子,比如有数字10,转为二进制码后为: 1 0 1 0 我们只需把 0 bit的位置全部用1填充,然后再把该二进制码加1就ok了.而x | (x + 1)正好可以把最右边的0置为1,可是

N皇后问题(位运算实现)

本文参考Matrix67的位运算相关的博文. 顺道列出Matrix67的位运算及其使用技巧 (一) (二) (三) (四),很不错的文章,非常值得一看. 主要就其中的N皇后问题,给出C++位运算实现版本以及注释分析. 皇后问题很经典,就不再赘述问题本身,解决皇后问题,一般采用的都是深搜DFS+回溯的方法,按照行(列)的顺序枚举每一个可以放置的情况,然后进行冲突判断,当前的放置是否合法,合法就继续搜索下一层,不合法就搜索就回溯.直到,找到一个合法的解,每一层都有一个皇后并且不发生冲突,这时候,放置

N皇后 八皇后 位运算解法

问题描述 什么是八皇后? 题目链接 N皇后 解法 ? 直接在N*N的棋盘上进行深搜,试探着下棋,也就是回溯法. ? 对于一个皇后来说,我们需要判断她的 八个方向 ,即 主对角线,副对角线,行,列 ? 1. 确定状态 ? 第一眼的感觉是要用 四个数组来储存情况,但时间上只需要三个 ,把 行 排除在外 ? 因为每次搜索的时候,都自动按行搜索,也就是变量 row ,每改变一次,代表了不同的行 ? 接下来,分析主对角线 ,对于N*N的矩阵来说,主对角线上的值满足 行和列的差为定值 ? 即,做减法得到的下

N皇后(回溯版+位运算版)

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

C位运算笔记(根据网上内容整理)1

什么是位运算?    程序中的所有数在计算机内存中都是以二进制的形式储存的.位运算说穿了,就是直接对整数在内存中的二进制位进行操作.由于位运算直接对内存数据进行操作,不需要转成十进制,因此处理速度非常快. 各种位运算的使用 === 1. and运算 ===(同真为真) and运算通常用于二进制取位操作,例如一个数 and 1的结果就是取二进制的最末位.这可以用来判断一个整数的奇偶,二进制的最末位为0表示该数为偶数,最末位为1表示该数为奇数. === 2. or运算 ===(一真为真) or运算通

位运算+枚举

位运算http://c.biancheng.net/cpp/html/101.html 在很多情况下(比如翻棋子)在搜索时涉及大量枚举往往导致超时,位运算则很好地解决了这个问题,方便又快捷 HDU  1882   Strange Billboard http://acm.hdu.edu.cn/showproblem.php?pid=1882 Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Ot