设为首页 加入收藏

TOP

BFS(四):搜索状态判重(一)
2019-07-10 12:10:28 】 浏览:38
Tags:BFS 搜索 状态

      在采用广度优先算法进行搜索时,一个需要重点注意的是在搜索过程中判重和去重。前面介绍的几个例子中,判重都较简单,如采用vis[]数组,若vis[i]==0,则i未访问过,i入队列;若vis[i]!=0,则i已访问过,不再重复访问。

      但在有些实际应用中,判重不是简单一个设置就可完成的。例如,给出一个由1、2、3、4、5、6组成的6位数,相邻的两个数字可以交换位置,问最少经过多少次交换,可以到达另一个目标6位数。例如:对于123456,最少经过两次交换,可以变成231456。 

      在解决这个问题时,一定要注意判重。例如:由123456可以变成213456,而213456又可以变成123456,这样形成了循环。因此,若某个状态已出现过,当前再出现这个状态时,应判定重复出现,不能入队进行处理。

      显然,这个问题中不能仿照以前的例子,定义一个vis[654322]数组,若某个状态231456出现就置vis[231456]=1。为什么呢?因为6个数字组成的不同状态最多6*5*4*3*2*1=720种,vis数组的元素个数是状态种数的900多倍,太浪费存储空间。

       在状态判重方法中,hash法是一种常用的方法。

【例1】最少交换。

      给出一个由1、2、3、4、5、6组成的6位数,相邻的两个数字可以交换位置,问最少经过多少次交换,可以到达另一个目标6位数。例如:对于123456,最少经过两次交换,可以变成231456。

      (1)编程思路。

       用广度优先搜索完成。在搜索中,需要解决状态判重问题。状态判重常用hash法。具体做法是:找到一种办法,把数字1~6的排列映射为一个整数num(0<=num<=(6!-1))。例如,排列123456
"font-family: 宋体;">”
映射为0、213456映射为1、132456映射为2、231456映射为3、…、654312映射为718、654321映射为719。
这样,每种状态就可以对应一个整数。反过来说,0~ (6!-1)之间的任一整数,也可以唯一对应一种状态。因此,可以定义一个数组hash[720](初始值全部为0,代表未出现过),某个状态next出现了,先求出其对应的整数值num,然后置hash[num]=1。这样,判断状态next是否出现过,先求出next对应的整数值num,若hash[num]!=0,则表示状态next出现过。 

      n! 与n个数字组成的全排列如何映射呢?我们以3个数字1、2、3组成的全排列来说明问题。

      设排列中所有数字满足从小到大排列,则称为正序。不是正序的排列中一定存在某个数字k后面有若干个数字比k小,比k小的数字个数n称为k的逆序个数。

      例如,123的各位逆序个数序列为:0,0,0。映射整数为:0=0*2!+0*1!+0*0!=0

                 132的各位逆序个数序列为:0,1,0。映射整数为:1=0*2!+1*1!+0*0!=1

                 213的各位逆序个数序列为:1,0,0。映射整数为:2=1*2!+0*1!+0*0!=2

                 231的各位逆序个数序列为:1,1,0。映射整数为:3=1*2!+1*1!+0*0!=3

                 312的各位逆序个数序列为:2,0,0。映射整数为:4=2*2!+0*1!+0*0!=4

                 321的各位逆序个数序列为:2,1,0。映射整数为:5=2*2!+1*1!+0*0!=5

       对6位数 654312而言,各位逆序个数序列为:5,4,3,2,0,0,应映射为:5*5!+4*4!+3*3!+2*2!+0*1!+0*0!=600+96+18+4=718。

      这实际上也很好理解,654312首位出现6,后面比它小的数字有1、2、3、4、5共5个,若首位出现6,则首位分别出现1、2、3、4和5的情况都出现过,才可能首位出现6,而对于6位数而言,首位出现1的情况有5!种,首位出现2的情况也是5!种,…,所以映射时首位5*5!。

      同理,可以理解次位是:逆序个数*4!,……。

      (2)源程序。

#include <stdio.h>

#include <string.h>

int fact[]={1,1,2,6,24,120};   //  对应0!,1!,2!,3!,4!,5!

int hash(char *s)                 // 把1..6的排列*s 映射为数字 0..(6!-1)

{   

       int i, j, temp, num;   

       num = 0;   

       for (i = 0; i <6-1; i++)

       {     

            temp = 0; 

            for (j = i + 1; j < 6; j++)

            {

                  if (s[j] < s[i])

                       temp++;  &nb
编程开发网

首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇BFS(五):八数码难题 (POJ 107.. 下一篇Can you answer these queries III

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

array(4) { ["type"]=> int(8) ["message"]=> string(24) "Undefined variable: jobs" ["file"]=> string(32) "/mnt/wp/cppentry/do/bencandy.php" ["line"]=> int(217) }