POJ 1077 解题报告

采用最简单的宽搜做法,用一个hash表保存已经搜索过的节点。状态值使用一个int,每三位对应序列中的一个数,最后五位存放0的位置,0和8都用000表示。在C++中会超时,在G++中9232K 782MS ,可以说效率是比较低的。

 

使用双向bfs,可以大大提高效率。注意我这里双向广度的循环是逐层的。我原来以为,如果不用逐层,两个搜索队列的层数可能不平衡,效率可能会降低,其实不逐节点扩展速度也不差。每次扩展完一层后都在队列中加个0标志。以下是双向bfs实现,结果用了47ms。

 


最有意思的是A*算法。A*算法的写法与一般的宽搜没什么区别,只是它的队列不是顺序的,而是优先队列。遍历时每次取优先级最高的节点。

优先级怎么确定呢?通常我们使用估值函数f=g+h。g是从难题节点到当前节点的遍历距离,h是当前节点到顺序节点(1 2 3 4 5 6 7 8 x)的距离估计。如果令h=0,就退化到普通的bfs。距离估计通常有两种:第一种直接比较两个节点的差异个数,第二种计算两个节点的曼哈顿距离。第二种方法使用效果较好,而且不包括数字0(即空块)的曼哈顿距离更好。估值函数中g的存在可以限制求出来的解不会太深,h可以加快搜索效率。如果给g和h加上权值,我们可以认为是求好的解(步数少)和快的解(搜索快,扩展节点少)的权衡。

还要注意g是应该在搜索过程中更新的,即发现扩展的节点比原来步数更少,应该更新。更新时不能直接修改优先队列中的数值,还应通知优先队列已经修改了,以便优先队列保持堆结构。这时判重的哈希表的节点中应该保存g。

如果完全忽略g,则哈希表中只需保存状态和方向。而队列中保存节点的曼哈顿距离。另外发现在求优先级时如果曼哈顿距离相等,再比较两者数字0处(即空块)的曼哈顿距离大小,小的优先会进一步减少扩展节点个数。如果数字0处曼哈顿距离仍相等,再比较二者的g大小,小的g优先,扩展节点个数会有非常微弱的减小。如果原来不计算g,为了微弱的性能提升,现在要比较还要维护,有点得不偿失。随机测试一千个序列,得到的平均扩展节点个数如下:

双向宽度优先搜索                              661

仅比较曼哈顿距离                              236

还比较数字0出的曼哈顿距离                173

还比较g                                           168

 

以下是比较曼哈顿距离再比较数字0出的曼哈顿距离的做法,在POJ上提交,620K 0MS。很奇怪的是代码提交到hdu1043和ustc1012都是错误的,以至于后来自己写了个special judge,对9!的全排列一一做了测试,也没发现问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值