期末复习之宽度优先搜索

宽度优先搜索的算法思想是比较简单了

关键就是如何在有限的时间内写出正确的宽度优先搜索才是重点

不过在开始还是要弄明白,看到一个题,到底是用宽度优先搜索还是用深度优先搜索,所以,我们要明白宽度优先搜索相对于深度优先搜索的优点

1.方便找到最优解(一般第一个解就是最优的,而深度优先搜索必须要遍历完所有可能解才能确定)

2.没有递归(所以状态转换更加直观明了)


所以宽度优先搜索适合处理:
迷宫类问题,魔板类问题(因为这些一般都要求最优解)


最简单的宽度优先搜索包括:

1.记录要走的下一个状态,并放入队列

2.在正确的状态终结搜索过程


对于第一点,如何记录当前状态,对于迷宫或魔板,一般是一个二维数组,当然也可以用字符串来保存,这里建议放入一个结构体中,方便记录状态的其他参数


一般宽度优先搜索还包括:
1.记录状态转移的路径

2.搜索的深度

路径可以通过一个数组放入结构体中,已知路径,深度当然也可以得到


优化:

1.去重(记录已经走过的状态)

无环迷宫当然不会有重复的状态,但是有环迷宫或者魔板一般都会出现重复的状态,所以如何去重是优化一个宽度优先搜索的关键,不过,对于一个魔板问题,给定一个状态,如何快速地确定这个状态是不是重复的,最简单有效的办法就是hash映射,但是如何把一个状态转换为数组的下标就是关键了,这里使用康托展开:

康拓展开,就是求一个全排列是第几个排列

比如给定1234的全排列,问3241是由小到大是第几个排列,可以看出,1234应该是第一个排列,而1243应该是第二个排列,4321应该是最后一个排列,也就是第4!个

而康托展开就给出了一个系统求全排列的方法:
X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0!

其中,a[n]表示比当前数小的数还有多少种(因为是由小到大)

比如1234,比1小的数没有,那么就是0,再看2,发现比2小的数只有1,但已经出现在第一位了,所以也是0,3,4同理,那么1234康托展开就是0*3!+0*2!+0*1!+0*0!=0

再比如1243,1,2都是0,再看4,比4小的只有3,那么1234的康托展开就是0*3!+0*2!+1*1!+0*0!=1


以下是一段12345678全排列的康托展开

int u[8] = {5040,720,120,24,6,2,1,1}; //预先保存好7!,6!之类的值
int cantor(int a[]) {
	int is_visited[9] = {0}; //未访问过的数字,1~8
	int num = 0; //康托展开的结果
	for(int i = 0; i < 8; i++) {
		int mid = a[i];
		int x = 0; //用来保存有多少个比当前数字小的数
		while(--mid) { //这个循环用来算出有多少个比当前数字小的数
			if(!is_visited[mid])
				x++;
		}
		is_visited[a[i]] = 1; //将访问过的数字置为1
		num += x * u[i]; 
	}
	return num;
}

这样,我们就可以把一个魔板状态压缩成一个小于8!=40320的数字,然后把这个作为数组下标来记录已经访问过的状态


一些注意事项:

1.全局变量的初始化

2.终结条件的准确判断(比深搜要方便)


一些错误类型:
1.如果超时,一般是对STL库使用过多,或者频繁调用函数

2.如果超内存,一般是搜索深度过深,这时要考虑到去重(或者是开了很大的数组)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值