回溯法、数独与N阶可达问题
回溯法是剪了枝的穷举,这是字面上的说法,不太好理解,不如讲解实例来的酸爽,于是引出了N阶可达问题:
有N个国家,每个国家有若干城市,小明要从中国(任意一个城市)出发,遍历所有国家(假设这个遍历顺序已经定了),最终到达美利坚(任意一个城市)。而城市之间有可能不可达,只有小明尝试过才知道(就是后面的check()函数),求满足要求的一条路径?

从上面的表述中我们已经嗅到了浓浓的穷举屌丝气质——遍历所有组合,但是我们的回溯思想总是基于这样一个简单的事实:如果当前选择导致你走进了死胡同,那么这个选择一定是错误的,同时基于这个错误的后续所有的选择都是错误而无意义的(剪枝)。道理的前半句表明我们要及时回溯,而后半句指出了这样做的优点是剪枝。比如小明要遍历中国---日本---美国,小明选择从中国武汉出发,这个选择是正确的还是错误的尚不明确,但是小明经过许多个check()之后发现,没有从武汉到日本任意一个城市的可达线路,这说明选择从武汉出发这个决定是错误的,应该回溯,重新选择一个中国的起点城市,小明在不知不觉中已经排除了形如(中国武汉市)---(日本XX市)---(美国XX市)的诸多组合,这就是所谓的剪了枝的穷举。
数独问题也是典型的N阶可达问题,下面以一个挖去64个洞的数独为例来具体说明,每个洞有1~9共9种可能性,一共要填64个洞,并且每填写好一个洞对后续的步骤会产生影响。

假设我们是从左到右,从上到下依次填写数字,那么此数独问题可以表达为如下的64阶可达问题:

根据回溯思想,采用递归函数(因为每层的情况是一样的,请读者思考如果不一样该如何编程)依次处理编号为0~80共计81个格子(代码略)。
下面我们用号称世界最难的数独题来测试一下程序,首先在程序同目录下建立sudoku.txt的文件,然后输入以下内容:
800000000
003600000
070090200
050007000
000045700
000100030
001000068
008500010
090000400
保存然后运行程序,得到如下结果。程序大约运行了60ms,在进行了49584次尝试之后找到了数独的解。

再来一发,号称专杀暴力破解的数独题试一下:

哇,好奇怪,世界最难数独都能在百毫秒内求解,为什么这个数独题居然花了大约37秒?莫非此数独真的有专杀暴力破解的神秘力量?前面我们说回溯只是做了剪枝的工作,下面这幅图展示了回溯到底做了什么:

剪枝后只用搜索红色部分
从本质上说,搜索是一维的,答案位于这个一维地图中的某处,你搜索速度的快慢取决于你的地图和答案在地图中的位置。前面我们尝试的顺序是123456789,现在我们尝试987654321这个搜索顺序,把程序修改一番,再次运行哪个两个数独,结果如下:

左为世界最难,右边为专杀暴力
上面我们只是改变了搜索顺序,只是一维地图的反转后搜索,相当于地图没变我们换了另一头搜索。在第一程序中,之所以很快能搜索到世界最难数独,而搜索专杀爆破很慢,是因为世界最难的答案离这一端很近,专杀爆破的答案离这一端很远。当我们在不改变地图的情况下换另一端开始搜索,就会出现上面的结果。
故在数独算法中,每个人都说自己的算法是高效的,并给出试验数据。实际上这个说法并不准确,只能说采用不同的方法,地图的规模不同,答案所在的位置也不同。对于同一个数独,你的算法你别人快并不能说明什么,因为必然存在专杀你这种搜索方法的数独存在。你的算法搜索的越快,换个姿势一定就会越慢。
回溯法、数独与N阶可达问题的更多相关文章
- P1074 靶形数独 dfs回溯法
题目描述 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他们想用数独来一比高低.但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教,Z 博士拿出了他最近发明的“靶 ...
- Leetcode之回溯法专题-37. 解数独(Sudoku Solver)
Leetcode之回溯法专题-37. 解数独(Sudoku Solver) 编写一个程序,通过已填充的空格来解决数独问题. 一个数独的解法需遵循如下规则: 数字 1-9 在每一行只能出现一次.数字 1 ...
- python常用算法(7)——动态规划,回溯法
引言:从斐波那契数列看动态规划 斐波那契数列:Fn = Fn-1 + Fn-2 ( n = 1,2 fib(1) = fib(2) = 1) 练习:使用递归和非递归的方法来求解斐波那契数 ...
- N-Queens And N-Queens II [LeetCode] + Generate Parentheses[LeetCode] + 回溯法
回溯法 百度百科:回溯法(探索与回溯法)是一种选优搜索法,按选优条件向前搜索,以达到目标.但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步又一次选择,这样的走不通就退回再走的技术为回溯法 ...
- 从Leetcode的Combination Sum系列谈起回溯法
在LeetCode上面有一组非常经典的题型--Combination Sum,从1到4.其实就是类似于给定一个数组和一个整数,然后求数组里面哪几个数的组合相加结果为给定的整数.在这个题型系列中,1.2 ...
- 五大常用算法之四:回溯法[zz]
http://www.cnblogs.com/steven_oyj/archive/2010/05/22/1741376.html 1.概念 回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试 ...
- 算法入门经典-第七章 例题7-4-1 拓展 n皇后问题 回溯法
实际上回溯法有暴力破解的意思在里面,解决一个问题,一路走到底,路无法通,返回寻找另 一条路. 回溯法可以解决很多的问题,如:N皇后问题和迷宫问题. 一.概念 回溯算法实际类似枚举的搜索尝试过程,主 ...
- 0-1背包问题——回溯法求解【Python】
回溯法求解0-1背包问题: 问题:背包大小 w,物品个数 n,每个物品的重量与价值分别对应 w[i] 与 v[i],求放入背包中物品的总价值最大. 回溯法核心:能进则进,进不了则换,换不了则退.(按照 ...
- 【LeetCode】回溯法 backtracking(共39题)
[10]Regular Expression Matching [17]Letter Combinations of a Phone Number [22]Generate Parentheses ( ...
随机推荐
- SQL点滴16—SQL分页语句总结
原文:SQL点滴16-SQL分页语句总结 今天对分页语句做一个简单的总结,他们大同小异的,只要理解其中一个其他的就很好理解了. 使用top选项 *from Orders orderid from Or ...
- POJ 1655 Balancing Act 焦点树
标题效果:鉴于一棵树.除去一个点之后,这棵树将成为一些中国联通的块.之后该点通过寻求取消最低形成块的最大数目. 思维:树DP思维.通过为每个子树尺寸的根节点深搜索确定.之后该节点然后除去,,还有剩下的 ...
- IntelliJ IDEA 发布13版本——创造java奇迹
IntelliJ IDEA被公认为业界最好的Java开发平台.此次发布的了13版本,更是集合了与Java EE.Android.Spring.Scala和Gradle最新合作与支持. Java EE ...
- 总结windows多线程同步互斥
windows多线程同步互斥--总结 我的windows多线程系列文章: windows多线程--原子操作 windows多线程同步--事件 windows多线程同步--互斥量 windows多线程同 ...
- ubuntu下无法打开windows下ntfs文件系统的解决方法
例如:/dev/sda5无法加载,使用如下命令 sudo ntfsfix /dev/sda5 如果命令没有安装,先安装(sudo apt-get install ntfsprogs).
- SQL随机生成数值字符
1.随机生成0~N之间的数值 ROUND(RAND()*N,0) 2.随机生成Min~Max之间的数值 ROUND(RAND()*(Max-Min),0)+Min 3.随机生成大写字母 CHAR(RO ...
- MongoDB:利用官方驱动改装为EF代码风格的MongoDB.Repository框架 二
本次改动的主要内容是实现MongoDB.Repository对MongoDBRef的支持. MongoDB对一对一,一对多,多对多关系的维护,官方推荐文档嵌入方式,反映到模型的设计如下: public ...
- Event对象的事件句柄
<html> <!-- onresize 事件会在窗口或框架被调整大小时发生 --> <!--onresize="alert('窗口的大小得到变化就会执行我') ...
- sublime text 3 插件:package control
sublime text 3安装package control import urllib.request,os,hashlib; h = 'eb2297e1a458f27d836c04bb0cbaf ...
- (转)cin、cin.get()、cin.getline()、getline()、gets()等函数的用法
学C++的时候,这几个输入函数弄的有点迷糊:这里做个小结,为了自己复习,也希望对后来者能有所帮助,如果有差错的地方还请各位多多指教(本文所有程序均通过VC 6.0运行)转载请保留作者信息:1.cin1 ...