AcWing 845. 八数码

题目传送门

一、理解与感悟

  1. 为什么将二维转一维?
    输入的是一个一维数据,就像是一个字符串。二维数组如果想对比每一次走完的棋盘是否一致,需要双重循环,比较麻烦,所以想出了一个用一维模拟二维的方法。

  2. 二维怎么转一维?
    一维下标=tx * 3 + ty

以行号、列号下标从(0)开始为例,就是行号( imes 3 +) 列号,比如:
行号为(1)(第二行),列号为(1)(第二列),那么(1 imes 3+1=4),就是在一维中是第(5)个(下标从(0)开始)。

  1. 一维如何还原成二维?
    int x = k / 3, y = k % 3;

二、完整代码

#include <bits/stdc++.h>

using namespace std;

int bfs(string s) {
    //终点的样子
    string end = "12345678x";
    //广度搜索套路
    queue<string> q;
    //将当前棋盘入队列
    q.push(s);
    //记录距离
    unordered_map<string, int> d;
    d[s] = 0; //s节点的距离是0,就是无需变动
    //dx数组
    int dx[] = {-1, 0, 1, 0};
    int dy[] = {0, 1, 0, -1};

    while (!q.empty()) {
        auto t = q.front();
        q.pop();
        //距离
        int dist = d[t];
        //判断是不是到目标值了,这里就能看出字符串模拟的好处了,要不就是两个二维数组的双重循环对比,麻烦。
        if (t == end) return dist;

        //状态转移
        int k = t.find('x');
        //一维数组转化为二维数组的坐标,是一个常用技巧
        //除3就是行,模3就是列,x,y下标从0开始
        int x = k / 3, y = k % 3;
        //四个方向
        for (int i = 0; i < 4; i++) {
            //走一步后的坐标
            int tx = x + dx[i], ty = y + dy[i];
            //不出界
            if (tx >= 0 && tx < 3 && ty >= 0 && ty < 3) {
                //调整位置
                //二维数组转一维数组的技巧
                swap(t[k], t[tx * 3 + ty]);
                if (!d.count(t)) {      //这个位置以前没有人走过
                    d[t] = dist + 1;    //距离需要+1
                    q.push(t);          //放入队列
                }
                swap(t[tx * 3 + ty], t[k]);
            }
        }
    }
    return -1;
}

int main() {
    //读入优化
    ios::sync_with_stdio(false);

    //开始的棋盘(字符之间用空格隔开,所以不能用string,需要cin+char解决)
    string s;
    char c;
    for (int i = 1; i <= 9; i++) {
        cin >> c;
        s += c;
    }
    //广度优先搜索
    cout << bfs(s) << endl;

    return 0;
}
原文地址:https://www.cnblogs.com/littlehb/p/15307374.html