A*(A - Star) - Eight - HDU - 1043
题意:
给 定 一 个 3 × 3 的 方 阵 , 包 含 数 字 1 给定一个3×3的方阵,包含数字1 给定一个3×3的方阵,包含数字1~ 8 , 以 及 字 符 ′ x ′ ( 表 示 空 位 ) , 每 次 操 作 通 过 上 下 左 右 移 动 数 字 到 ′ x ′ 所 在 的 空 位 上 , 使 得 最 终 呈 现 一 个 有 序 的 9 宫 格 。 8,以及字符'x'(表示空位),\\每次操作通过上下左右移动数字到'x'所在的空位上,使得最终呈现一个有序的9宫格。 8,以及字符′x′(表示空位),每次操作通过上下左右移动数字到′x′所在的空位上,使得最终呈现一个有序的9宫格。
要 通 过 最 少 的 操 作 次 数 使 得 9 宫 格 复 位 , 输 出 一 个 最 少 操 作 方 案 。 要通过最少的操作次数使得9宫格复位,输出一个最少操作方案。 要通过最少的操作次数使得9宫格复位,输出一个最少操作方案。
若 不 能 使 9 宫 格 有 序 , 则 输 出 ′ ′ u n s o l v a b l e ′ ′ . 若不能使9宫格有序,则输出''unsolvable''. 若不能使9宫格有序,则输出′′unsolvable′′.
′ u ′ 表 示 将 ′ x ′ 向 上 移 动 , ′ d ′ 表 示 将 ′ x ′ 向 下 移 动 , ′ r ′ 表 示 将 ′ x ′ 向 右 移 动 , ′ l ′ 表 示 将 ′ x ′ 向 左 移 动 。 'u'表示将'x'向上移动,'d'表示将'x'向下移动,'r'表示将'x'向右移动,'l'表示将'x'向左移动。 ′u′表示将′x′向上移动,′d′表示将′x′向下移动,′r′表示将′x′向右移动,′l′表示将′x′向左移动。
Sample Input
2 3 4 1 5 x 7 6 8
Sample Output
ullddrurdllurdruldr
Time limit: 5000 ms
Memory limit: 32768 kB
Special judge : Yes
分析:
与 — — 与—— 与——《POJ 1077 - 八数码》 题 意 一 致 , 仅 仅 是 需 要 输 出 具 体 操 作 方 案 。 题意一致,仅仅是需要输出具体操作方案。 题意一致,仅仅是需要输出具体操作方案。
有 一 个 重 要 的 性 质 : 若 序 列 中 的 逆 序 对 数 量 是 奇 数 , 则 无 法 使 得 九 宫 格 有 序 。 有一个重要的性质:若序列中的逆序对数量是奇数,则无法使得九宫格有序。 有一个重要的性质:若序列中的逆序对数量是奇数,则无法使得九宫格有序。
设 启 发 函 数 f ( s t ) 表 示 从 状 态 s t 到 终 点 状 态 需 要 的 最 少 操 作 次 数 。 设启发函数f(st)表示从状态st到终点状态需要的最少操作次数。 设启发函数f(st)表示从状态st到终点状态需要的最少操作次数。
d [ s t ] 表 示 从 起 始 状 态 到 当 前 状 态 s t 已 经 进 行 的 操 作 次 数 。 d[st]表示从起始状态到当前状态st已经进行的操作次数。 d[st]表示从起始状态到当前状态st已经进行的操作次数。
对 于 每 次 操 作 , 我 们 优 先 选 择 d [ s t ] + f ( s t ) 更 小 的 状 态 进 行 操 作 , 当 终 点 状 态 e n d 第 一 次 出 队 时 , 就 得 到 最 少 操 作 的 一 种 方 案 。 对于每次操作,我们优先选择d[st]+f(st)更小的状态进行操作,\\当终点状态end第一次出队时,就得到最少操作的一种方案。 对于每次操作,我们优先选择d[st]+f(st)更小的状态进行操作,当终点状态end第一次出队时,就得到最少操作的一种方案。
具体落实:
① 、 因 为 要 求 出 具 体 方 案 , 定 义 哈 希 表 p r e , 把 每 一 种 状 态 与 前 驱 状 态 和 从 前 驱 状 态 转 移 而 来 的 操 作 对 应 起 来 。 ①、因为要求出具体方案,定义哈希表pre,把每一种状态与前驱状态和从前驱状态转移而来的操作对应起来。 ①、因为要求出具体方案,定义哈希表pre,把每一种状态与前驱状态和从前驱状态转移而来的操作对应起来。
② 、 距 离 数 组 d , 存 储 从 起 始 状 态 到 当 前 状 态 已 经 进 行 的 操 作 次 数 。 ②、距离数组d,存储从起始状态到当前状态已经进行的操作次数。 ②、距离数组d,存储从起始状态到当前状态已经进行的操作次数。
③ 、 小 根 堆 h e a p , 以 d [ s t ] + f ( s t ) 为 第 一 关 键 字 , 堆 中 元 素 是 一 个 估 计 距 离 和 对 应 状 态 的 一 个 二 元 组 。 ③、小根堆heap,以d[st]+f(st)为第一关键字,堆中元素是一个估计距离和对应状态的一个二元组。 ③、小根堆heap,以d[st]+f(st)为第一关键字,堆中元素是一个估计距离和对应状态的一个二元组。
④ 、 先 将 初 始 状 态 入 堆 , 每 次 从 堆 中 取 出 堆 顶 元 素 进 行 扩 展 , 若 某 个 状 态 未 搜 索 过 , 或 者 重 复 搜 索 到 当 前 状 态 时 距 离 更 优 , 就 把 该 状 态 入 堆 。 ④、先将初始状态入堆,每次从堆中取出堆顶元素进行扩展,\\\qquad若某个状态未搜索过,或者重复搜索到当前状态时距离更优,就把该状态入堆。 ④、先将初始状态入堆,每次从堆中取出堆顶元素进行扩展,若某个状态未搜索过,或者重复搜索到当前状态时距离更优,就把该状态入堆。
⑤ 、 当 第 一 次 从 堆 顶 得 到 终 点 状 态 , 提 前 退 出 循 环 , 根 据 p r e 表 倒 推 具 体 操 作 。 ⑤、当第一次从堆顶得到终点状态,提前退出循环,根据pre表倒推具体操作。 ⑤、当第一次从堆顶得到终点状态,提前退出循环,根据pre表倒推具体操作。
⑥ 、 f 函 数 的 具 体 实 现 : 对 当 前 状 态 的 每 一 个 数 字 , 计 算 该 数 字 当 前 位 置 与 其 在 终 点 状 态 中 的 位 置 的 曼 哈 顿 距 离 。 ⑥、f函数的具体实现:对当前状态的每一个数字,计算该数字当前位置与其在终点状态中的位置的曼哈顿距离。 ⑥、f函数的具体实现:对当前状态的每一个数字,计算该数字当前位置与其在终点状态中的位置的曼哈顿距离。
注意: 本 题 是 多 组 测 试 数 组 ! ! ! 本题是多组测试数组!!! 本题是多组测试数组!!!
代码:
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
#include<queue>
#include<unordered_map>
#define P pair<int,string>
using namespace std;
int f(string start)
{
int res=0;
for(int i=0;i<9;i++)
if(start[i]!='x')
{
int t=start[i]-'1';
res+=abs(i/3-t/3)+abs(i%3-t%3);
}
return res;
}
string bfs(string start)
{
int dir[4][2]={{-1,0},{1,0},{0,-1},{0,1}};
char op[4]={'u','d','l','r'};
string end="12345678x";
unordered_map<string,pair<string,char>> pre;
unordered_map<string,int> d;
priority_queue<P,vector<P>,greater<P>> heap;
d[start]=0;
heap.push(make_pair(f(start),start));
while(heap.size())
{
P t=heap.top();
heap.pop();
string state=t.second;
int step=d[state];
if(state==end) break;
int x,y;
for(int i=0;i<9;i++)
if(state[i]=='x')
{
x=i/3,y=i%3;
break;
}
string source=state;
for(int i=0;i<4;i++)
{
int a=x+dir[i][0],b=y+dir[i][1];
if(a>=0&&a<3&&b>=0&&b<3)
{
swap(state[x*3+y],state[a*3+b]);
if(!d.count(state)||d[state]>step+1)
{
d[state]=step+1;
heap.push(make_pair(d[state]+f(state),state));
pre[state]=make_pair(source,op[i]);
}
swap(state[x*3+y],state[a*3+b]);
}
}
}
string res;
while(start!=end)
{
res+=pre[end].second;
end=pre[end].first;
}
reverse(res.begin(),res.end());
return res;
}
int main()
{
string c;
while(cin>>c)
{
string seq,start;
start+=c;
if(c!="x") seq+=c; //用于计算逆序对数量
for(int i=0;i<8;i++)
{
cin>>c;
start+=c;
if(c!="x") seq+=c;
}
int cnt=0;
for(int i=0;i<7;i++)
for(int j=i+1;j<8;j++)
if(seq[i]>seq[j])
cnt++;
if(cnt%2) cout<<"unsolvable"<<endl;
else cout<<bfs(start)<<endl;
}
return 0;
}