最小步数模型
什么是最小步数模型:
我们知一般的DFS求最短路通常是从棋盘中的一个点a到一个点b
但这里的最小步数是把一个棋盘看做一个状态,求从状态a到状态b的最小步数
总的来说最小步数模型是求状态之间转换的最短距离。
最短路就是求点与点之间转换的最短距离。
关于字典序最小的路径问题:
- 一般来说,输出最字典序最小或者最大的路径不会增加题目的难度和复杂度。出题者因为懒,不搞speci judge,让我们输出唯一的答案,搞了这么一出。
- 一般我们按照特定的顺序执行操纵,不需要增加多余的代码,就可以实现最小的字典序,本题由于A的字典序最小,我们只需要贪心的选择A,次B,最后C就好了。
- 一般不要想着证明上面那种方法的正确性,很麻烦,还容易把自己绕晕。
- 另外,本题中,不可以逆向搜索!因为逆向搜索不好找最小的字典序路径!因为正向字典序最小的路径不一定是逆向字典序最大的路径!
关于输出路径:
- 在输出路径路径的时候,我们一般都需要一个pre数组来距离当前状态是从哪一个状态转移过来的,关于pre的设置,是一个需要认真考量的地方!
- 本题中,加入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
本题由如下坑点,恶心了我半个小时
- 题目输入是目标状态序列,而不是原原本的两行四列的数组,所以不需要转换!
- 题目要求的是“源状态12345678”到输入的目标状态的转换,而不是目标状态到源状态的转化。