最小步数模型

什么是最小步数模型:

我们知一般的DFS求最短路通常是从棋盘中的一个点a到一个点b

但这里的最小步数是把一个棋盘看做一个状态,求从状态a到状态b的最小步数

总的来说最小步数模型是求状态之间转换的最短距离。

最短路就是求点与点之间转换的最短距离。

1107. 魔板 - AcWing题库

关于字典序最小的路径问题: 

  1. 一般来说,输出最字典序最小或者最大的路径不会增加题目的难度和复杂度。出题者因为懒,不搞speci judge,让我们输出唯一的答案,搞了这么一出。
  2. 一般我们按照特定的顺序执行操纵,不需要增加多余的代码,就可以实现最小的字典序,本题由于A的字典序最小,我们只需要贪心的选择A,次B,最后C就好了。
  3. 一般不要想着证明上面那种方法的正确性,很麻烦,还容易把自己绕晕。
  4. 另外,本题中,不可以逆向搜索!因为逆向搜索不好找最小的字典序路径!因为正向字典序最小的路径不一定是逆向字典序最大的路径!

关于输出路径: 

  1. 在输出路径路径的时候,我们一般都需要一个pre数组来距离当前状态是从哪一个状态转移过来的,关于pre的设置,是一个需要认真考量的地方!
  2. 本题中,加入b是由a转移过来的。首先,pre[b] = a,但这样够吗?不行!我们还需要在转移的过程中保存下来我们的路径,而不单单是父节点的信息。即pre[b]={a, path}

总结下来就是:

pre[子节点] = {父节点, 路径}

#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <unordered_map>

using namespace std;

string start = "12345678", st_end;
unordered_map<string, int> dist;
unordered_map<string, pair<string, char> > pre;
string m[3];
int a[8];
int change1[8] = {3, 0, 1, 2, 5, 6, 7, 4};
int change2[8] = {0, 6, 1, 3, 4, 2, 5, 7};

string move0(string s) 
{
    string news = s;
    reverse(news.begin(), news.end());
    return news;
}

string move1(string s)
{
    string news = "";
    for(int i = 0; i < 8; i ++ )    news += s[change1[i]];
    return news;
}

string move2(string s)
{
    string news = "";
    for(int i = 0; i < 8; i ++ )    news += s[change2[i]];
    return news;
}

void bfs()
{    
    dist[start] = 0;
    
    if(start == st_end) return ;
    
    queue<string> q;
    q.push(start);

    while(q.size())
    {
        string t = q.front();   q.pop();
        m[0] = move0(t);
        m[1] = move1(t);
        m[2] = move2(t);
        
        for(int i = 0; i < 3; i ++ )
        {
            if(dist.count(m[i]))    continue;
            dist[m[i]] = dist[t] + 1;
            pre[m[i]] = {t, 'A' + i};
            q.push(m[i]);
            if(m[i] == st_end) return ;
            
            // cout << "bdebug" <<  m[i] << endl;
        }
    }
}

int main()
{
    for(int i = 0; i < 8; i ++ )    cin >> a[i];
    for(int i = 0; i < 8; i ++ )    st_end += a[i] + '0';
    // cout << "st_end: " << st_end << endl;
    
    // cout << "next:" << endl;
    // cout << move0(st_end) << endl;
    // cout << move1(st_end) << endl;
    // cout << move2(st_end) << endl;
    
    
    bfs();
    
    int ans = dist[st_end];
    // cout << "res: " << dist[start] << endl;
    string res = "";
    // cout << "str: " << st_end << endl;
    while(st_end != start)
    {
        res += pre[st_end].second;
        // cout << "str: " << pre[st_end].first << endl;
        st_end = pre[st_end].first;
    }
    
    cout << ans << endl;
    if(ans)
    {
        reverse(res.begin(), res.end());
        cout << res << endl;
    }
    // cout << "---------" << endl;
    // string p = "57263184";
    // cout << move0(p) << endl;
    
    return 0;
}

//BCABCCB

本题由如下坑点,恶心了我半个小时

  1. 题目输入是目标状态序列,而不是原原本的两行四列的数组,所以不需要转换!
  2. 题目要求的是“源状态12345678”到输入的目标状态的转换,而不是目标状态到源状态的转化。
posted @ 2022-05-05 08:41  光風霽月  阅读(27)  评论(0编辑  收藏  举报