说到位运算的经典应用,不得不说N皇后问题。

学过程序设计的都知道N皇后问题,没听过也没关系。很简单,最传统的的N皇后问题是这个样子的,给你一个n * n大小的board,让你放n个皇后(国际象棋),要满足任意两个皇后不能在一条水平线上,不能在一条垂直线上,也不能在一条45度的斜线上。听起来似乎和数独挺像,其实N皇后的条件更苛刻,除了水平垂直线外,数独只有两条对角线,而N皇后有很多条斜线,这点需要注意。

为了判断程序的正确性,正好leetcode上有道题可以测试N-Queens II,也是最经典的N皇后问题,给出n求得摆法的数量。

常规解法


一个很显而易见的解法是递归求解,从上到下一行一行摆下去,并且记录每行摆的位置,因为下一行摆放的位置要根据上面的摆放情况来确定。这里我们定义了一个pos数组,pos[1]的值表示摆在第一行的皇后所在的列,pos[2]的值表示摆在第二行的皇后所在的列,以此类推,所以摆到第n行时,上面摆放的皇后的位置就可以很容易地得到,从而可以进行判断(该行什么位置可以摆放)。

当摆放到第r行时,我们遍历该行所有的n个位置,判断每个位置(r行c列)是否可以摆放,需要与前面摆放的每个皇后比较,是否在一条水平线上(这是不可能的),是否在一条垂直线上,是否在一条斜线上:

function check(r, c) {
  for (var i = 1; i < r; i++) {
    if (Math.abs(i - r) === Math.abs(pos[i] - c)) // 一条斜线上
      return true;

    if (c === pos[i]) // 一条竖直线上
      return true;
  }

  return false;
}

整个代码也就显而易见了:

var pos;

function check(r, c) {
  for (var i = 1; i < r; i++) {
    if (Math.abs(i - r) === Math.abs(pos[i] - c)) // 一条斜线上
      return true;

    if (c === pos[i]) // 一条竖直线上
      return true;
  }

  return false;
}

function dfs(r, n) {
  if (r === n + 1)
    return 1;

  var ans = 0;

  for (var i = 1; i <= n; i++) {
    if (check(r, i)) continue;
    pos[r] = i;
    ans += dfs(r + 1, n);
  }

  return ans;
}

var totalNQueens = function(n) {
  pos = [];
  return dfs(1, n);
};

位运算解法


在读下文前,请先阅读【位运算经典应用】标志位与掩码会事半功倍。

位运算解法,递归是避免不了的,能优化的在于check()函数部分。

假设一个4 * 4的board,我们在第一行第三列上摆了个皇后,其实它已经把第一行之后的6个位置给ko掉了:

- - o -
- X X X
X - X -
- - X -

我们用flag=2(1<<2)记录第一行摆下的这个位置,在第二行中,很显然(1<<2)这个位置已经不能摆了,而(2<<1)这个位置也不能摆,(2>>1)这个位置也不能摆。如果要在第二行右起第1个摆下皇后,我们用flag2=1(1<<1)记录这个决定,我们只需用flag2和以上算出来的不能摆的位置去做个与运算,看看有没有冲突即可。结果(2>>1)&(1<<1)得到了非0的数,表示两者冲突,所以该位置摆放失败。

假设我们接着在第二行左起第一个摆放了皇后,对于第三行的摆放来说,第一行摆的皇后对它还是有影响的,比如它不能摆在第三行左起第一个位置,因为(2<<2)&(1<<4)!==0,冲突。而(2<<2)正是在第二排摆放决策中(2<<1)<<1。看到这里你也许应该有了思路,没错,我们可以维护三个数,l, r, c, 用来表示该行被右上角斜线,左上角斜线,正上方直线所影响而不能摆放的位置。三个数每次与欲摆放的位置作与运算,求解是否冲突,如没有,l和r分别左移右移后继续递归。

function dfs(l, r, c, index, n) {
  if (index === n)
    return 1;

  var ans = 0;

  for (var i = 0; i < n; i++) {
    var tmp = 1 << i;
    if ((tmp & l) || (tmp & r) || (tmp & c)) continue;
    ans += dfs((tmp | l) << 1, (tmp | r) >> 1, tmp | c, index + 1, n);
  }

  return ans;
}

var totalNQueens = function(n) {
  return dfs(0, 0, 0, 0, n);
};

【位运算经典应用】 N皇后问题的更多相关文章

  1. Java位运算经典实例

    一 源码.反码.补码 正数的源码.反码.补码相同,例如5:            5的源码:101            5的反码:101            5的补码:101 负数的源码.反码.补 ...

  2. N皇后-位运算优化

    N皇后问题 时间限制: 5 Sec  内存限制: 128 MB 题目描述 魔法世界历史上曾经出现过一个伟大的罗马共和时期,出于权力平衡的目的,当时的政治理论家波利比奥斯指出:“事涉每个人的权利,绝不应 ...

  3. JavaScript 位运算总结&拾遗

    最近补充了一些位运算的知识,深感位运算的博大精深,此文作为这个系列的总结篇,在此回顾下所学的位运算知识和应用,同时也补充下前文中没有提到的一些位运算知识. 把一个数变为大于等于该数的最小的2的幂 一个 ...

  4. 位运算总结&拾遗

    JavaScript 位运算总结&拾遗 最近补充了一些位运算的知识,深感位运算的博大精深,此文作为这个系列的总结篇,在此回顾下所学的位运算知识和应用,同时也补充下前文中没有提到的一些位运算知识 ...

  5. leetcode - 位运算题目汇总(下)

    接上文leetcode - 位运算题目汇总(上),继续来切leetcode中Bit Manipulation下的题目. Bitwise AND of Numbers Range 给出一个范围,[m, ...

  6. 【C语言】位运算

    编写一个函数getbits,从一个16位的单元中取出某几位(即该几位保留原值,其余位0).函数调用形式为getbits(value,n1,2).----简单题目遇到想不到的问题 c语言位运算经典问题: ...

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

    本文参考Matrix67的位运算相关的博文. 顺道列出Matrix67的位运算及其使用技巧 (一) (二) (三) (四),很不错的文章,非常值得一看. 主要就其中的N皇后问题,给出C++位运算实现版 ...

  8. [CODEVS1295]N皇后(位运算+搜索)

    题目描述 Description 在n×n格的棋盘上放置彼此不受攻击的n个皇后.按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子.n后问题等价于再n×n的棋盘上放置n个皇后,任 ...

  9. C语言位运算、移位运算 经典示例

    概述: C语言的位级运算可以运用到任何“整数”的数据类型上,如char.short.int.long.long long.或者unsigned这样的限定词.基本的位运算有与.或.非.异或等等. C语言 ...

随机推荐

  1. python里面出现中文的时候报错 'ascii' codec can't encode characters in position

    编码问题,在头部添加 import sys reload(sys) sys.setdefaultencoding( "utf-8" ) http://www.xuebuyuan.c ...

  2. hadoop2.2.0伪分布式搭建1--准备Linux环境

    1.0修改网关 点击VMware快捷方式,右键打开文件所在位置 -> 双击vmnetcfg.exe -> VMnet1 host-only ->修改subnet ip 设置网段:19 ...

  3. msysGit管理GitHub代码

    msysGit管理GitHub代码   代码的管理,在日常开发中是很重要的环节,程序员的修炼三部曲——版本控制,单元测试,项目自动化. 本篇就简单的说说通过msysGit来管理GitHub中的代码,实 ...

  4. MySQL入门(四)

    我好久没有写这个系列了,也确实不知道写什么.回首我学习数据库的路,最开始搞Oracle的时候其实没有搞懂Oracle,也不知道学了什么,后来学习MySQL尤其是InnoDB才大概入了门了.我最开始学习 ...

  5. openstack-kilo--issue(九) heat stacks topology中图形无法正常显示

    ======声明======= 欢迎转载:转载请注明出处 http://www.cnblogs.com/horizonli/p/6186581.html ==========环境=========== ...

  6. [转]正确配置Linux系统ulimit值的方法

    在Linux下面部署应用的时候,有时候会遇上Socket/File: Can't open so many files的问题:这个值也会影响服务器的最大并发数,其实Linux是有文件句柄限制的,而且L ...

  7. Java api 入门教程 之 JAVA的包装类

    Java语言是一个面向对象的语言,但是Java中的基本数据类型却是不面向对象的,这在实际使用时存在很多的不便,为了解决这个不足,在设计类时为每个基本数据类型设计了一个对应的类进行代表,这样八个和基本数 ...

  8. android switch(String)错误:Cannot switch on a value of type String for source level below 1.7

    switch语句的判断条件可以接受int,byte,char,short,不能接受其他类型只有JDK版本1.7以上才可以支持String 设置如下可解决问题:(若没有JDK1.7版,可下载一下安装)菜 ...

  9. TFS代码变更和工作项关联,为系统变更提供完美的跟踪轨迹

    TFS是微软的应用软件生命周期管理(ALM)的解决方案产品,相比我们常见的一些ALM产品,例如HP ALM, IBM Rational, Atlanssian Jira等,其最大的区别在于TFS将软件 ...

  10. 大话设计模式C++版——简单工厂模式

    简单工厂模式应该是所有设计模式中最简单,也最基础的一种模式,以下是一个简单的采用工厂模式写一个加减法的计算器. 1.抽象接口类——依赖倒转原则(高层和底层都要依赖于抽象,针对接口编程) class I ...