首先给一个hdu的测试地方,注意是多组输入。或者看单组的这里,poj的测试数据比较水,没有杭电的强
八数码的两种搜索方式:
A*
- 对A*的理解:
需要保存每个搜索过的状态,防止重复。所以很大一部分时间都放在了判重上,而且记录状态这一点使得占用的内存空间比较大,不适合大一点的数据。 - A*主要过程与方法:
A*属于启发式搜索,所以精华都在启发上。
首先介绍一下估价函数:
f(n) = g(n) + h(n)。f(n)就代表了n点的估价值,g(n)代表从起点到当前点的实际价值,h(n)代表从当前点到终点的估值。
对于每个点,用估价函数都可以求出来一个估价值,其实这个估价值就是估计从起点到终点且经过当前点的估计路径长度。所以估价值越接近正确的值,那么搜索效率越高。分析估价函数其实只是分析h(n)。
分两种情况:
1)估计值小于真实值(即属于h(n)的某些步没有计算)
此时考虑一个估计值最小的点,当从这个点搜索到下一个点时,在估计路径的第一步就跑到了已经走过的路径,那么必然可以碰到某一步时候,总的值变大了(之前在h(n)中没有计算,现在到了g(n)中必须计算),此时这个点的下一个点就有可能不在队列的头部,也就是说很多点都会被搜索。那么这种情况的特点就是,不会漏掉最优解,但是程序的效率比较低。如果每个点的h(n)都等于零,那么没有就是没有启发,这种情况也就是普通的bfs,即bfs会搜索到最优点,但是搜索盲目性很大
2)估计值大于真实值(即属于h(n)的某些步多次计算)
类比上述情况,当一个点搜索至下一个点时,如果发生了f(n)减少,那么延伸出来的点将会继续是最小的,那么有些点可能一直没有机会被搜索到。这种情况的特点就是搜索效率高,但是可能会漏掉最优解。
- 过程:
在进行A*搜索时,需要维护两个集合,搜索过的和待搜索的。开始时先将起点放到待搜索的集合中,然后开始搜索。每次从等待搜索的集合中取出估价值最小的,将这个点放入已经搜索过的集合,并且观察这个点能延伸到得下一点(集):如果在待搜索列表中,那么看一下两个的g(n)值,取两个中较小的那一个放入待搜索队列;如果在已经搜索过的集合中,那么抛弃这个点;否则,将这个点放到待搜索列表中。重复以上过程,直至找到答案。
在编程中,显然是用priority_queue来维护待搜索集合(重复点没法取出,有一些永远不会搜索到的点会在队列中),用hash来维护已经搜索过集合,搜索方式采用bfs广度优先搜索
#include <cstdio>
#include <ctime>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <string>
#include <set>
#include <stack>
#include <map>
#include <cmath>
#include <vector>
#include <iostream>
#include <algorithm>
#include <bitset>
#include <fstream>
#include <list>
using namespace std;
const int MAXN = 100000;
bool used[9], hash_table[362879 + 1];
int fn[9], x[9], y[9];
int dx[] = {0, -1, 1, 0}, dy[] = {-1, 0, 0, 1};
char direction[] = {'l', 'u', 'd', 'r'};
struct Node
{
int a[3][3], zx, zy;
int hash_value, f, step, preindex;
Node* pre;
int operator< (const Node& a) const
{
return step + f > a.step + a.f;
}