标签:uva 10181 15-puzzle problem 15数码
题目大意:
给你一个八数码的序列,要求你在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*)
标签:uva 10181 15-puzzle problem 15数码
原文地址:http://blog.csdn.net/helloworld10086/article/details/41908903