A*(A - Star) - Eight - HDU - 1043

A*(A - Star) - Eight - HDU - 1043

题意:

给 定 一 个 3 × 3 的 方 阵 , 包 含 数 字 1 给定一个3×3的方阵,包含数字1 3×31~ 8 , 以 及 字 符 ′ x ′ ( 表 示 空 位 ) , 每 次 操 作 通 过 上 下 左 右 移 动 数 字 到 ′ x ′ 所 在 的 空 位 上 , 使 得 最 终 呈 现 一 个 有 序 的 9 宫 格 。 8,以及字符'x'(表示空位),\\每次操作通过上下左右移动数字到'x'所在的空位上,使得最终呈现一个有序的9宫格。 8x()x使9

要 通 过 最 少 的 操 作 次 数 使 得 9 宫 格 复 位 , 输 出 一 个 最 少 操 作 方 案 。 要通过最少的操作次数使得9宫格复位,输出一个最少操作方案。 使9

若 不 能 使 9 宫 格 有 序 , 则 输 出 ′ ′ u n s o l v a b l e ′ ′ . 若不能使9宫格有序,则输出''unsolvable''. 使9unsolvable.

′ u ′ 表 示 将 ′ x ′ 向 上 移 动 , ′ d ′ 表 示 将 ′ x ′ 向 下 移 动 , ′ r ′ 表 示 将 ′ x ′ 向 右 移 动 , ′ l ′ 表 示 将 ′ x ′ 向 左 移 动 。 'u'表示将'x'向上移动,'d'表示将'x'向下移动,'r'表示将'x'向右移动,'l'表示将'x'向左移动。 uxdxrxlx

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)为第一关键字,堆中元素是一个估计距离和对应状态的一个二元组。 heapd[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;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值