179. 八数码 题解

题目描述

avataravatar

解题思路

本题采用两种做法 1.普通BFS;(不做过多阐述,仅在下方给出代码) 2.A*寻路算法

解题思路参考https://www.acwing.com/solution/content/35528/ (非常详细) A*寻路算法定义参考视频

代码

cpp
// 普通BFS解法
#include <bits/stdc++.h>
using namespace std;

char d[5] = "drul";
int dx[] = {1,0,-1,0}, dy[] = {0,1,0,-1};
string ed = "12345678x";  // 目标状态
unordered_map<string,int> p;  // 记录操作类型
unordered_map<string,string> pre;  // 记录路径

bool bfs(string s)
{
    queue<string>q;
    q.push(s);

    while (q.size())
    {
        auto t = q.front();
        q.pop();
        string h = t;  // 备份
        if (t == ed)    return true;

        int k = t.find('x');
        int x = k / 3, y = k % 3;  // 计算出序列转为矩阵后的坐标

        for (int i = 0; i < 4; i++)
        {
            int a = x + dx[i], b = y + dy[i];
            if (a >= 0 && a < 3 && b >= 0 && b < 3)
            {
                int temp = a * 3 + b;  // 计算转为序列后'x'所处的位置
                swap(t[k], t[temp]);
                if (!p.count(t))  // 如果之前没有访问过
                {
                    p[t] = i;  // 记录操作类型
                    pre[t] = h;  // 记录前驱序列
                    q.push(t);
                }
                swap(t[k], t[temp]);  // 还原为原序列,以便进行其他操作
            }            
        }
    }
    return false;
}

int main()
{
    string s, c;
    for (int i = 0; i < 9; i++) cin >> c, s += c;

    if (bfs(s))
    {
        vector<char>arr;
        string t = ed;
        while (t != s)  // 输出路径
        {
            arr.push_back(d[p[t]]);  // d[]是为了取出操作类型
            t = pre[t];  // 转移到下一个序列
        }
        reverse(arr.begin(), arr.end());
        for (auto x : arr)  cout << x;
    }
    else cout << "unsolvable" << endl;
    
    return 0;
}
cpp
// A*寻路解法
#include <bits/stdc++.h>
using namespace std;

int f(string state)  // 估计函数
{
    int res = 0;
    for (int i = 0; i < state.size(); i++)
        if (state[i] != 'x')  
        {
            int t = state[i] - '1';  // -'1'是因为“12...x”是从1开始的
            res += abs(i / 3 - t / 3) + abs(i % 3 - t % 3);  // 曼哈顿距离
        }
    return res;
}

string bfs(string start)
{
    int dx[] = {-1,0,1,0}, dy[] = {0,1,0,-1};
    char op[4] = {'u','r','d','l'};  // 要将操作数组与坐标变化数组一一对应
    string end = "12345678x";  // 终点
    unordered_map<string, pair<string,char> > prev;  // 存储一个元素由哪种状态,经过哪种操作得来
    unordered_map<string, int> dist;  
    // 小根堆,将元素的估计终点距离从小到大排序
    priority_queue<pair<int, string>, vector<pair<int, string>>, greater<pair<int, string>> > q;

    q.push({f(start), start});  // 加入起点
    dist[start] = 0;  // 起点到起点的距离为0

    while (q.size())
    {
        auto t = q.top();
        q.pop();

        string state = t.second;
        if (state == end)  break;  // 终点出列的话就退出

        int x, y;  // 查找x的横纵坐标
        for (int i = 0; i < state.size(); 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 + dx[i], b = y + dy[i];
            if (a >= 0 && a < 3 && b >= 0 && b < 3)  // 矩阵边界
            {
                swap(state[x * 3 + y], state[a * 3 + b]);
                if (!dist.count(state) || dist[state] > dist[source] + 1)  // 如果没有被记录或者小于记录值
                {
                    dist[state] = dist[source] + 1;  // 更新距离
                    prev[state] = {source, op[i]};  // 标记由哪种状态转移而来,并且记录执行的操作
                    q.push({f(state) + dist[state], state});
                }
                swap(state[x * 3 + y], state[a * 3 + b]);  // 因为要扩展到四个方向,所以要还原
            }
        }
    }

    string res;
    while (end != start)
    {
        res += prev[end].second;
        end = prev[end].first;
    }
    reverse(res.begin(), res.end());
    return res; 
}


int main()
{
    string g, c, seq;
    while (cin >> c)
    {
        g += c;
        if (c != "x")   seq += c;
    }

    int t = 0;  // 统计逆序对的数量
    for (int i = 0; i < seq.size(); i++)
        for (int j = i + 1; j < seq.size(); j++)
            if (seq[i] > seq[j])  t++;

    if (t % 2)  cout << "unsolvable";  // 如果逆序对为奇数,就不可能抵达终点
    else  cout << bfs(g);

    return 0;
}
P5367 【模板】康托展开 题解
190. 字串变换 题解
Valaxy v1.0.0-rc.3 驱动|主题-Yunv1.0.0-rc.3