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 ;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
八数码问题是一种经典的搜索问题,可以使用A*算法来解决。以下是一个使用Python实现的八数码问题A*算法的示例代码: ``` from queue import PriorityQueue # 用于表示八数码问题中的状态 class State: def __init__(self, board): self.board = board self.moves = [] # 获取当前状态的估价函数值 def heuristic(self): # 计算曼哈顿距离 distance = 0 for i in range(3): for j in range(3): if self.board[i][j] != 0: row = (self.board[i][j] - 1) // 3 col = (self.board[i][j] - 1) % 3 distance += abs(row - i) + abs(col - j) return distance # 获取当前状态是否为目标状态 def is_goal(self): return self.board == [[1, 2, 3], [4, 5, 6], [7, 8, 0]] # 获取当前状态的后继状态 def successors(self): moves = [] i, j = self.get_blank_position() if i > 0: move = self.move_blank(i, j, i - 1, j) moves.append(move) if i < 2: move = self.move_blank(i, j, i + 1, j) moves.append(move) if j > 0: move = self.move_blank(i, j, i, j - 1) moves.append(move) if j < 2: move = self.move_blank(i, j, i, j + 1) moves.append(move) return moves # 获取当前状态中的空白位置 def get_blank_position(self): for i in range(3): for j in range(3): if self.board[i][j] == 0: return i, j # 移动空白位置 def move_blank(self, i1, j1, i2, j2): new_board = [row[:] for row in self.board] new_board[i1][j1], new_board[i2][j2] = new_board[i2][j2], new_board[i1][j1] new_state = State(new_board) new_state.moves = self.moves + [(i1, j1, i2, j2)] return new_state # 获取当前状态的代价 def cost(self): return len(self.moves) # 重载小于运算符,用于优先队列中的比较 def __lt__(self, other): return self.cost() + self.heuristic() < other.cost() + other.heuristic() # A*算法 def astar(start_state): # 创建优先队列 pq = PriorityQueue() # 将初始状态加入队列 pq.put(start_state) # 创建已访问状态集合 visited = set() # 循环搜索 while not pq.empty(): # 取出队列中的当前状态 state = pq.get() # 如果当前状态已经访问过,跳过 if str(state.board) in visited: continue # 将当前状态标记为已访问 visited.add(str(state.board)) # 如果当前状态为目标状态,返回结果 if state.is_goal(): return state.moves # 获取当前状态的后继状态 successors = state.successors() # 将后继状态加入优先队列中 for s in successors: if str(s.board) not in visited: pq.put(s) # 如果搜索失败,返回空列表 return [] # 测试 board = [[2, 3, 6], [1, 5, 0], [4, 7, 8]] start_state = State(board) moves = astar(start_state) print(moves) ``` 在上述代码中,State类表示八数码问题中的状态,包含当前状态的棋盘布局和到达当前状态所需的移动序列。heuristic()方法计算当前状态的估价函数值,即曼哈顿距离;is_goal()方法判断当前状态是否为目标状态;successors()方法获取当前状态的后继状态;get_blank_position()方法获取当前状态中的空白位置;move_blank()方法移动空白位置;cost()方法获取当前状态的代价,即到达当前状态所需的移动步数;__lt__()方法重载小于运算符,用于优先队列中的比较。astar()函数是A*算法的实现,使用优先队列来保存待访问的状态,使用visited集合来保存已访问过的状态。在搜索过程中,每次从优先队列中取出当前状态,将其后继状态加入优先队列中,直到找到目标状态或队列为空为止。最后,将搜索到的移动序列输出。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值