首页 > 代码库 > UVA - 10181 15-Puzzle Problem(15数码 A*)

UVA - 10181 15-Puzzle Problem(15数码 A*)

题目大意:

给你一个八数码的序列,要求你在50步之内还原成,初始的形状,如果可以就输出其路径,否则就输出 This puzzle is not solvable.


解析:

这题是用A*算法实现的,上网查了很多资料,了解了什么是A*算法。

A*算法,实质上是根据估价函数进行bfs,每次都选取估价最小的状态,进行移动。

其中 f(n) 是从初始点经由节点n到目标点的估价函数,
g(n) 是在状态空间中从初始节点到n节点的实际代价,
h(n) 是从n到目标节点最佳路径的估计代价

f(n) = g(n) + h(n)

g(n)在该题中是当前的移动步数,h(n)是当前到状态到终点的曼哈顿距离


还有就是如何判断,15数码是否能满足条件。

设 e 表示空滑块所在的行,n 为在当前滑块之后出现小于当前滑块数值的滑块数目,N 为: 
        15          15  
 N =   ∑  ni =   ∑  ni  
        i = 1       i = 2  
如果 N + e 为偶数,则当前局面有解,否则无解。如以下局面:  
 13 10 11 6  

 5  7  4  8  

 1  12 14 9   

 3  15 2  0   
 则有小于 13 并在之后出现滑块的数目为 12,小于 10 并在之后出现的滑块数目为 9,类似的,可以得到  
 其他滑块的 n 值分别为 9(11),5(6),4(5),4(7),3(4),3(8),0(1),3(12),  
 3(14),2(9),1(3),1(15),0(2)。所有 n 值的和为 N = 59,空滑块在第 4 行,故 e =   4,则 N + e = 63,为奇数,则以上局面不可解。


该代码被我修改后很快了总共耗时才0.068s

#include <string>
#include <queue>
#include <map>
#include <set>
#include <cstring>
#include <cstdlib>
#include <cstdio>
using namespace std;
int goal[18][2];
set<string> vis;
int dx[] = {0,0,1,-1};
int dy[] = {1,-1,0,0};
struct state {
	int h , step , x , y ;
	int board[4][4];
	string mov;
	bool operator < (const state &rhs) const {
		return h + step > rhs.h + rhs.step;
	}
};
inline int distanc(int x, int y, int xx, int yy) {
	return abs(x - xx) + abs(y - yy);
}
int get_h(int b[][4]) {
	int sum = 0;
	for (int i = 0; i < 4; i++) {
		for (int j = 0; j < 4; j++) {
			if (b[i][j] == 0 || (i == goal[b[i][j]][0] && j == goal[b[i][j]][1]))
				continue;
			sum += distanc(i, j, goal[b[i][j]][0], goal[b[i][j]][1]);
		}
	}
	return sum*4;
}
bool isAnswer(int b[][4]){
	int sum = 0 , z = 0 , x , y ;
	int tmp[16];
	for (int i = 0; i < 4; i++){
		for (int j = 0; j < 4; j++){
			tmp[z++] = b[i][j];
			if (b[i][j] == 0)
				x = i, y = j;
		}
	}
	for (int i = 0; i < 16; i++){
		for (int j = i + 1; j < 16; j++){
			if (tmp[j] < tmp[i] && tmp[j] )
				sum++;
		}
	}
	if ((sum + x) % 2 == 0)
		return false;
	return true;
}
bool try_to_insert(int b[][4]) {
	string st;
	for(int i = 0; i < 4; i++) {
		for(int j = 0; j < 4; j++) {
			st.push_back((char)(b[i][j]+'0'));
		}
	}
	if(vis.count(st)) {
		return false;
	}
	return true;
}
bool Astart(state a) {
	priority_queue <state> q;
	vis.clear();
	int x, y;
	state u, v;
	q.push(a);
	while (!q.empty()){
		u = q.top();
		q.pop();
		for (int i = 0; i < 4; i++){
			x = u.x + dx[i];
			y = u.y + dy[i];
			if (x >= 0 && y >= 0 && x < 4 && y < 4){
				v = u;
				swap(v.board[x][y], v.board[u.x][u.y]);
				v.step = u.step + 1;
				if (v.step > 50) 
					continue;
				if (i == 0) v.mov.push_back('R');
				else if (i == 1) v.mov.push_back('L');
				else if (i == 2) v.mov.push_back('D');
				else if (i == 3) v.mov.push_back('U');
				if (v.mov.size() >= 2) {
					int back1 = v.mov.size() - 1;
					int back2 = v.mov.size() - 2;
					if((v.mov[back1] == 'U' && v.mov[back2] == 'D')
						||(v.mov[back1] == 'D' && v.mov[back2] == 'U')
						||(v.mov[back1] == 'L' && v.mov[back2] == 'R')
						||(v.mov[back1] == 'R' && v.mov[back2] == 'L'))
					continue;
				}
				v.h = get_h(v.board);
				v.x = x;
				v.y = y;
				if (v.h == 0){
					printf("%s\n",v.mov.c_str());
					return true;
				}
				if(try_to_insert(v.board)) {
					q.push(v);
				}
			}
		}
	}
	return false;
}
int main() {
	int n = 1, t;
	memset(goal,0,sizeof(goal));
	for (int i = 0; i < 4; i++){
		for (int j = 0; j < 4; j++){
			goal[n][0] = i;
			goal[n++][1] = j;
		}
	}
	scanf("%d",&t);
	while (t--) {
		state start;
		for (int i = 0; i < 4; i++){
			for (int j = 0; j < 4; j++){
				scanf("%d",&start.board[i][j]);
				if (start.board[i][j] == 0)
					start.x = i, start.y = j;
			}
		}
		if (!isAnswer(start.board)) {
			printf("This puzzle is not solvable.\n");
			continue;
		}
		start.h = 0;
		start.step = 0;
		bool final_res = Astart(start);
		if (!final_res)
			printf("This puzzle is not solvable.\n");
	}
	return 0;
}

UVA - 10181 15-Puzzle Problem(15数码 A*)