题意大致就是让你复原一个八数码拼图,输出具体路径
思路:这里使用的是A*算法,A*算法可能比较陌生,迪杰斯克拉法求最短路相比大家都比较熟悉,A*算法就是他的进化,或者说dij算法是A*算法中把预估函数看为0得到的算法,
A*算法的主体是一个路径计算函数:f=g+h,其中g表示的从起始点到某一点的耗费距离,h则表示的是从当前点到目标点的预估耗费,一般在搜图中使用曼哈顿距离,而在本题中则是计算八数码中每个点复原所耗费的最小距离。
然后就跟bfs一样的模板了
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
const int maxn = 4e5 + 10;
struct node
{
int f[3][3];
int x, y;
int h, g;
int hashnum;
bool operator <(const node a)const
{
return g + h > a.g + a.h;
}
};
struct path
{
int pre;
char c;
}pre[maxn];
int ha[9] = {40320,5040,720,120,24,6,2,1,1};
int dir[4][2] = { {-1,0},{1,0},{0,-1},{0,1} };
char d[10] = "udlr";
int vis[maxn];
void print(int x)//因为你是先找,所以要先倒溯到起始点然后输出路径
{
if (pre[x].pre == -1) return;
print(pre[x].pre);
printf("%c", pre[x].c);
}
int gethash(node e)//利用康拓展卡来进行哈希表。
{
int a[9];
int cnt = 0, k = 0;;
int ans = 0;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
a[cnt++] = e.f[i][j];
for (int i = 0; i < 9; i++)
{
k = 0;
for (int j = i + 1; j < 9; j++)
{
if (a[i] > a[j]) k++;
}
ans += k * ha[i];
}
return ans;
}
int geth(node e)//计算A*算法中的h函数
{
int ans = 0;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
if(e.f[i][j])
ans += abs(i - (e.f[i][j] - 1) / 3) + abs(j - (e.f[i][j] - 1) % 3);
}
}
return ans;
}
void astar(node e)
{
int t = 0;
memset(vis, 0, sizeof(vis));
node a;
int cnt = 1, xx, yy;
for (int i = 0; i < 9; i++)
a.f[i / 3][i % 3] = (i + 1) % 9;
int end = gethash(a);
e.hashnum = gethash(e);
e.g = 0, e.h = geth(e);
vis[e.hashnum] = 1;
pre[e.hashnum].pre = -1;
if (e.hashnum == end)
{
printf("\n");
return;
}
priority_queue<node>que;
que.push(e);
while(!que.empty())
{
e=que.top();
que.pop();
for(int i=0;i<4;i++)
{
xx=e.x+dir[i][0];
yy=e.y+dir[i][1];
if(xx<0||yy<0||xx>=3||yy>=3)continue;
a=e;
swap(a.f[e.x][e.y],a.f[xx][yy]);
int k=gethash(a);
if(vis[k])continue;
vis[k]=1;
a.hashnum=k;
a.x=xx;
a.y=yy;
a.g++;
a.h=geth(a);
pre[k].pre=e.hashnum;
pre[k].c=d[i];
if(k==end)
{
print(k);
printf("\n");
return ;
}
que.push(a);
}
}
}
int main()
{
string a;
while (getline(cin, a))
{
node e;
int n = a.size();
for (int i = 0, j = 0; i < n; i++)
{
if (a[i] == ' ') continue;
if (a[i] == 'x')
{
e.x = j / 3;
e.y = j % 3;
e.f[j / 3][j % 3] = 0;
}
else e.f[j / 3][j % 3] = a[i] - '0';
j++;
}
int k = 0;
for (int i = 0; i < 9; i++)//计算这个八数码中的逆序数,如果逆序数为奇数则判定为不可解决,(从题意中可得出)
{
if (e.f[i/3][i%3] == 0) continue;
for (int j = 0; j < i; j++)
{
if (e.f[j/3][j%3] == 0) continue;
if (e.f[j/3][j%3] > e.f[i/3][i%3]) k++;
}
}
if (k & 1)
printf("unsolvable\n");
else
astar(e);
//a.clear();
}
//system("system");
return 0;
}