AcWing 845.八数码
原题链接
题解
要求最小的交换次数,那么就可以使用bfs来做,可以把每一次的交换之后的图抽象成一个节点,那么每次转移状态就相当于向下一个节点走,走过的边的权重为1(不是在原来的3*3的图上做bfs),这样就可以使用dfs做了
需要解决的问题是:
①每一个状态该如何表示
②如何记录每个状态的距离
③什么情况下表示不能后形成最终的状态
解决方式
①可以把二维数组转化成字符串的形式,每一步形成的字符串就是一个状态(每一个状态不要重复的经过,判定就好)
②因为是使用的字符串表示的状态,以前做bfs的时候,都是单一的点或者是坐标,这里就使用哈希表的方式,将字符串转化成对应的下标(以后这种类似的情况下类比也可以使用哈希表的方式)
③当队列为空了,还是没有找到最终状态,就表示不能够达到这个最终的状态(因为已经将所有的状态枚举了)
接下来就是常规的dfs的过程了,这道题最难的地方就在于上述的几点以及抽象的表示
图示

代码如下
#include <iostream>
#include <algorithm>
#include <queue>
#include <unordered_map>
using namespace std;
int bfs(string start){
    string end = "12345678x";//定义最终状态
    queue<string> q;
    unordered_map<string, int> dis;
    int dx[4] = {0, 0, -1, 1}, dy[4] = {1, -1, 0, 0};
    q.push(start); dis[start] = 0;
    while(q.size()){
        auto t = q.front(); q.pop();
        if(t == end) return dis[t];
        int j = t.find('x');
        for(int i = 0; i < 4; ++i){
            //这里还是转化成二维数组做方便一点,因为直接在字符串中x的左右最近的点不一定能够走到还又要判定
            int x = j / 3 + dx[i], y = j % 3 + dy[i];
            if(x >= 0 && x < 3 && y >= 0 && y < 3){
                string s = t;
                swap(s[x * 3 + y], s[j]);
                if(!dis.count(s)){
                    dis[s] = dis[t] + 1;
                    q.push(s);
                }
            }
        }
    }
    return -1;
}
int main(){
    string start;
    for(int i = 0; i < 9; ++i){
        char ch; cin >> ch;
        start += ch;
    }
    cout << bfs(start) << endl;
    return 0;
}
这个题目中,每一个变化都是代表着一种状态,难在不知道如何表示状态(这里是使用字符串表示每一个图),当把所有的状态枚举结束之后,还是没有出现结果,此时说明了这个输入的初始状态是不能够转化成最终的状态。搜索并不一定要在现有的图上做,如果每一步会改变图,就可以把每一次改变的图抽象成一个结点,用某一种状态去表示,然后结点和结点之间就构成了一张新的图。
    如有错误,欢迎指正!
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号