POJ1077、HDU1043 Eight 八数码问题:双向BFS、A*

Problem Address:http://poj.org/problem?id=1077

Problem Address:http://acm.hdu.edu.cn/showproblem.php?pid=1043


【前言】


第一次写八数码问题。

这份代码写得也是够残的。

还好效率不是很低。

HDU的数据明显比POJ的强。而且HDU里有初末状态相同的情况,而POJ没有。

这也是后来改成双向A*之后在HDU上狂WA的原因。

提交了20多次之后才发现真相= =


【思路】


典型的八数码问题。

九宫格中有1-8的数字,空出一位用于数字的移动。求从一个状态转移到另一个状态的路径。

具体的存储方式可以到网上搜一下。

以下简单介绍几种应用于八数码问题的搜索算法。


DFS:

由于不知道步数,所以用DFS是不可取的。


BFS:

从初始状态开始,每一步向四个方向扩展。直到达到末状态。

简单的BFS使用的空间很大,时间也很大,当步数增加时节点数膨胀很快。

因为,就八数码问题,一般的BFS不太可取。


双向BFS:

从初状态和末状态同时开始扩展。扩展方式与一般BFS相同。

直到两者有交点时停止查找。

相比于一般的BFS,双向BFS无论是在空间还是时间方面,都有很大的提升。


A*启发式搜索:

利用启发式函数 f'() = a*g'() + b*h'(),g'()表示当前以使用代价(可用步数表示),h'()表示达到目标还需花费的代价(可用manhattan距离或者相同位置不同数字的个数表示)。

启发式搜索相对于前面的搜索提升是巨大的。访问的结点很少,空间和时间的使用也因此很少。

可以这样看,BFS都是A*的一种特殊形式。

当b=0且a!=0时,f'() = a*g'(),即只于结点所耗步数有关,即为一般的BFS。

所以,通过改变a和b的值可以在一定程度上提升搜索效率。


双向A*:

A*与双向BFS的结合。


【代码】


这份代码是双向A*,但是通过改变里面的参数可以实现BFS、双向BFS、A*等。


#include 
#include 
using namespace std;

const int maxhash = 400000;

int start, end = 123456789;

int le[9] = {1,10,100,1000,10000,100000,1000000,10000000,100000000};
int te[9] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320};

int manhattan[9][9] = {
	{0,1,2,1,2,3,2,3,4},
	{1,0,1,2,1,2,3,2,3},
	{2,1,0,3,2,1,4,3,2},
	{1,2,3,0,1,2,1,2,3},
	{2,1,2,1,0,1,2,1,2},
	{3,2,1,2,1,0,3,2,1},
	{2,3,4,1,2,3,0,1,2},
	{3,2,3,2,1,2,1,0,1},
	{4,3,2,3,2,1,2,1,0}};

int re[9];

struct HASH
{
	bool visited;
	int value;
	int pre;
	char op;
	bool inqueue;
	int g;
	int h;
	bool opposite;
}hash[maxhash];

struct cmp
{
	bool operator()(const int &a, const int &b)
	{
		int fa = hash[a].g + 10*hash[a].h;
		int fb = hash[b].g + 10*hash[b].h;
		return fa>fb;
	}
};

char get_opposite(char op)
{
	switch(op)
	{
	case 'u':
		return 'd';
	case 'd':
		return 'u';
	case 'l':
		return 'r';
	case 'r':
		return 'l';
	default:
		return 'x';
	}
}

priority_queue, cmp> q;

void init_hash()
{
	int i;
	for (i=0; i=0; i--)
	{
		if (i+1=0; i--)
	{
		temp[i] = x%10;
		x /= 10;
	}
	int sum = 0;
	for (i=1; i<8; i++)
	{
		for (j=0; j=0; i--)
		{
			if (i+1=0; i--)
		{
			if (i+1=0; i--)
	{
		temp[i] = x%10;
		x /= 10;
	}
	for (i=1; i<8; i++)
	{
		k = 0;
		for (j=0; jhash[pre].g+1)
		{
			hash[hx].g = hash[pre].g + 1;
			hash[hx].op = op;
			hash[hx].pre = pre;
			if (!hash[hx].inqueue)
			{
				hash[hx].inqueue = true;
				q.push(hx);
			}
		}
	}
	return false;
}

bool expand(int t)
{
	int j;
	int temp = t%10;
	int ht = get_hash(t);
	hash[ht].inqueue = false;
	if (temp>3)
	{
		j = move('u', t);
		if (valid(j, 'u', ht)) return true;
	}
	if (temp%3!=0)
	{
		j = move('r', t);
		if (valid(j, 'r', ht)) return true;
	}
	if (temp<7)
	{
		j = move('d', t);
		if (valid(j, 'd', ht)) return true;
	}
	if (temp%3!=1)
	{
		j = move('l', t);
		if (valid(j, 'l', ht)) return true;
	}
	return false;
}

int main()
{
	char str[100];
	int i, j;
	int t;
	while(cin.getline(str, 100))
	{
		start = 0;
		for (i=0,j=0; str[i]!='\0'; i++)
		{
			if (str[i]!=' ')
			{
				if (str[i]=='x')
				{
					t = j + 1;
				}
				else
				{
					start = start*10 + (str[i]-'0');
					j++;
				}
			}
		}
		start = start*10 + t;
		if (!check_even(start))
		{
			printf("unsolvable\n");
			continue;
		}
		if (start==end)
		{
			printf("\n");
			continue;
		}
		init_hash();
		init_rank(start);
		while(!q.empty()) q.pop();
		valid(start, 'x', -1);
		valid(end, 'x', -1);
		while(!q.empty())
		{
			int ht = q.top();
			t = hash[ht].value;
			q.pop();
			if (expand(t)) break;
		}
	}
	return 0;
}




你可能感兴趣的:(搜索)