八数码 (Astar)

题目描述

在一个 3×3 的网格中,1∼8 这 8个数字和一个 X 恰好不重不漏地分布在这 3×3 的网格中。

例如:

1 2 3
X 4 6
7 5 8

在游戏过程中,可以把 X 与其上、下、左、右四个方向之一的数字交换(如果存在)。

我们的目的是通过交换,使得网格变为如下排列(称为正确排列):

1 2 3
4 5 6
7 8 X

例如,示例中图形就可以通过让 X 先后与右、下、右三个方向的数字交换成功得到正确排列。

交换过程如下:

1 2 3   1 2 3   1 2 3   1 2 3
X 4 6   4 X 6   4 5 6   4 5 6
7 5 8   7 5 8   7 X 8   7 8 X

把 X 与上下左右方向数字交换的行动记录为 udlr

现在,给你一个初始网格,请你通过最少的移动次数,得到正确排列。

输入格式

输入占一行,将 3×33×3 的初始网格描绘出来。

例如,如果初始网格如下所示:

1 2 3 
x 4 6 
7 5 8 

则输入为:1 2 3 x 4 6 7 5 8

输出格式

输出占一行,包含一个字符串,表示得到正确排列的完整行动记录。

如果答案不唯一,输出任意一种合法方案即可。

如果不存在解决方案,则输出 unsolvable

输入样例:

2  3  4  1  5  x  7  6  8 

输出样例

ullddrurdllurdruldr

思路

首先,八数码有解判断的必要条件:逆序对的个数若为偶数个时有解,若为奇数个则无解。详见图(借,有问题则删,https://www.acwing.com/solution/content/35528/)

八数码 (Astar)_第1张图片


其次,对于使用astar优化时,因为当我们移动一个点的时候,只能上下左右间移动,因此 f(n) 预估距离的设定,这里定义为当前图各个点到最终图各个点的哈夫曼距离之和。

参考代码:

#include 

#define io ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)

#define LL long long

#define x first

#define y second

#define PII pair

#define PIS pair

#define PIII pair

#define PDD pair

using namespace std;

const int INF=0x3f3f3f3f;

const int N=1005;

const int M=1e7;

const int mod=1e9+7;

  

/*

    八数码有解判断的必要条件:逆序对的个数若为偶数个时有解,若为奇数个则无解。


*/

  

//当前点到终点的预估距离,每个位置点距离自己正确位置的哈夫曼距离和

// abs(xs-xe)+abs(ys-ye);

int f(string state)

{

    int res = 0;

    int len=state.length();

    for(int i=0;i dis; //到这个状态的实际距离

    map >pre; //到这个状态的,前个状态和动作

  

    priority_queue,greater >q;

    q.push({f(start),start});

    dis[start]=0;

    while(q.size())

    {

        auto tmp=q.top();

        q.pop();

  

        //当前状态

        string state=tmp.y;

        //到达最终状态,退出

        if(state==end)break;

  

        //获取当前步数

        int step=dis[state];

        int x,y;

        //找到操作"X"的位置

        for(int i=0;i<9;i++)

        {

            if(state[i]=='x')

            {

                x=i/3;

                y=i%3;

                break;

            }

        }

        //存储当前状态

        string source=state;

  

        //开始向4个方向扩展

        for(int i=0,nx,ny;i<4;i++)

        {

            nx=x+dir[i][0];

            ny=y+dir[i][1];

            if(nx<0 || nx>=3 || ny<0 || ny>=3)continue;

  

            //交换2点位置

            swap(state[x*3+y],state[nx*3+ny]);

            /*

                更新状态的2种情况:

                1.若移动后的状态不存在

                2.若移动后的状态所用步数大于当前状态,则表示移动后的状态下都可以被更新

            */

            if(dis.count(state)==0 || dis[state]>step+1)

            {

                //更新实际距离

                dis[state]=step+1;

                //记录前驱,{前一个状态,动作}

                pre[state]={source,go[i]};

                q.push({dis[state]+f(state),state});

            }

  

            //回溯2点位置

            swap(state[x*3+y],state[nx*3+ny]);

        }

    }

  

    //反向遍历操作结果

    string res="";

    while(end!=start)

    {

        res+=pre[end].y;

        end=pre[end].x;

    }

    reverse(res.begin(),res.end());

    return res;

}

int main()

{

     char c;

     string str; //用来判断逆序对

     for(int i=0;i<9;i++)

     {

         cin>>c;

         if(c!='x')str+=c;

         start+=c;

     }

  

     int cnt=0; //逆序对个数

    for(int i=0;istr[j])cnt++;

        }

    }

    //奇数个,无解

    if(cnt&1)cout<<"unsolvable\n";

    //偶数个,存在解

    else cout<

你可能感兴趣的:(c++,算法)