179. 八数码 A* Astar 算法 双重优化 BFS 优先队列 实际距离 + 预计距离

题目

在这里插入图片描述

题解思路

看前须知
在搜索中常用的 一维表示二维 行数为 k / i 列数为 k % i

在这里插入图片描述
A*一般用在一定到达的情况。(否则复杂度比普通bfs还高,因为使用优先队列)

所以 我们先特判掉 无法到达的情况 。 改变X的位置 逆序数对要么不变要么加2减2 。 所以当题目的逆序对数为奇数时直接特判了。

A*(边权非负)
在普通bfs的基础上引入了一个启发函数,用每个状态的曼哈顿总数来表示预计距离 , 普通的加入层数则是实际距离 。 再将启发函数值 ( 预计距离 + 实际距离 )作为优先队列的判断条件 , 从而让答案更容易找出 。
就相当于让bfs尽可能的往正确的方向走,不让他在错误的方向走太久。

实现上也有点类似dij,并且除了终点之外的点 第一次出队 不一定是最小值(多次更新) 。即被启发函数从错误的方向带回来了。
而终点一定是最小值,因为启发函数就是面向终点的情况写的。

代码实现上

先判断逆序数,再用一个哈希的last存入变成这个状态之前的状态以及操作编号(逆向输出反转即是答案,老套路了,这样省空间)

再写一个判断曼哈顿距离的函数 ,利用之前的一维化二维性质可以更方便。
优先队列为启发函数值为变量,再模拟题中的操作。

当条件更优以及没更新过就入队(别忘了加曼哈顿距离),更新last,更新dis实际距离即可。

参考文章1
参考文章2

AC代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>
#include <map>
#include <string>
#include <unordered_map>
using namespace std;


string  A , B = "123456789";

int dx[4] = {0,0,-1,1} ;
int dy[4] = {1,-1,0,0} ;
char opr[5] = "rlud" ;
int mhd ( string s )
{
    int cnt = 0 ;
    for (int i = 0 ; i < 9 ; i++ )
    {
        int tt = s[i] - '1'  ;
        cnt += abs( i/3 - tt/3) + abs( i% 3 - tt%3 );
    }
    return cnt ;
}

struct node
{
    string x ;
    int w ;
    bool operator < (const node &other) const
    {
        return w > other.w;
    }

};

void bfs()
{
    unordered_map <string  , int   >  dis ;
    unordered_map < string , pair<char , string > > last ;
    priority_queue <node> q ;
    dis[A] =  0  ;
    q.push({A,mhd(A)}) ;
    while ( ! q.empty() )
    {
        node tmp = q.top();
        q.pop();

        if ( tmp.x == B )
            break;
        int pot  ;
        for (int i = 0 ; i < 9 ; i++ )
        {
            if ( tmp.x[i] == '9' )
            {
                pot = i ;
                break ;
            }
        }
        for (int i = 0 ; i < 4 ; i++ )
        {
            int fx = pot/3 + dx[i] ;
            int fy = pot%3 + dy[i] ;
            if ( fx >= 0 && fy >= 0 && fx < 3 && fy < 3 )
            {
                string pk = tmp.x ;
                swap(pk[pot],pk[fx*3+fy]) ;
                if ( ! dis.count(pk) || dis[pk] > dis[tmp.x] + 1 )
                {
                    dis[pk] = dis[tmp.x] + 1 ;
                    last[pk].first = opr[i] ;
                    last[pk].second = tmp.x ;
                    q.push({pk,mhd(pk) + dis[pk]}) ;
                }
            }
        }
    }
    string tp = B ;
    string ans = "";
    while ( tp != A )
    {
        ans += last[tp].first ;
        tp = last[tp].second ;
    }
    reverse(ans.begin(),ans.end()) ;
    cout << ans << "\n" ;

}

int main ()
{
    ios::sync_with_stdio(false);

    for ( int i = 0 ; i < 9 ; i++ )
    {
        char ch[2] ;
        cin >> ch ;
        if (ch[0] == 'x' )
            A += '9' ;
        else
            A += ch[0] ;

    }
    int cnt = 0 ;
    for (int i = 0 ; i < 9 ; i++ )
        for (int j = 0 ; j < i ; j++ )
            if ( A[i] != '9' && A[j] != '9' && A[j] > A[i] )
            {
                cnt ++ ;
            }
    if ( cnt % 2 )
        cout << "unsolvable\n" ;
    else
        bfs();
    return 0 ;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值