UVA1601 The Morning after Halloween(状态压缩,双向bfs,A*)
本题属于搜索类题,通过bfs去枚举状态,从而找到解。然而该题的状态数上限是256的三次方,状态转移有5的3次方个转移方法,转移代价高。解决方法:
1、因为题目保证每2×2个方格一定有一个障碍,所以可以将所有空格提取出来。目的是在状态转移时不用每次去判断条件(越界、有障碍)。
具体步骤:
(1)、是将原图的每个空格的坐标保存到另外一个数组,并将空格编号。
(2)、开辟一个二维数组G[x][y],x表示当前空格的编号,y表示转向,取值0~4,表示有几种转移路径可选,G[x][y]的值表示在x空格通过y转移路径(你无需直到y的具体转移方法)能到达的另一个空格的编号。用trans[x]来表示编号为x的空格的转移路径的数目。
这样在搜索的过程中就无需进行重复判断了。
2、双向bfs,将起点和终点同时放进队列。起点的step设为0,终点的step设为1.将从起点出发的vis值设为1,从终点出发的vis值设为2,将没有访问过的vis值设为0;当现节点的vis+相邻节点的vis==3时,答案即为step之和。
3、A*算法。
(1)将起点加入open_list
(2)当open_list不为空:
(3)选取open_list中f值最小的节点
(4)如果该节点为终点,则打印路径,结束循环;
(5)遍历当前节点的周围节点:
如果周围的节点在close_list中或者是不可到达的点,则跳过;
如果周围的节点不在open_list中,加入open_list,计算f值,并将当前节点作为其父节点;
如果周围节点在open_list中,比较经由当前节点的f值和原f值:如果经由当前节点的f值更小,则更新该节点的父节点为当前节点,更新f值;
(6)如果满足条件2,重复上述3~5步骤
锦上添花之举:
1、用编码(encoding)的方法将每个状态(节点的位置)压缩成一个整数再放进队列;取出来的时候再解码(decoding)。具体方法:
题目最大图为16×16,如果给空格编号,则每个小写字母可能出现的位置所在的最大空格标号为16×16=256=2的8次方,所以可以用一个8位的2进制数去表示一个点的位置。
A*算法实现代码如下(320ms):
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdarg>
#include<queue>
using namespace std;
char dataset[20][20];
int G[200][5];
int trans[200];
int w,h,n;
int s[3],t[3];
int x[200],y[200],id[20][20];
int dx[5]={
0,1,-1,0,0};
int dy[5]={
0,0,0,1,-1};
const int d=0xff;
int dist_2[200][3];//图上每个点到终点的距离
int min_f[200][200][200];//记录最小f值,用于比较原f和现有最优f
struct Node{
int state;//状态
// int fx;//估值函数,选取节点包含的三个点到终点的最大距离作为启发函数h(x)的值
int gx;//已知代价函数
int hx;//启发函数
Node(int a,int b,int c):state(a),gx(b),hx(c){
};
bool operator <