原题链接:https://www.acwing.com/problem/content/1109/
这里不但要输出步数,还要记录字典序最小的操作序列。
字典序最小:每次操作的时候,优先选择A,再选择B,再选C。
dist仅仅和输出步数有关,pre仅仅和输出操作序列有关。
char数组转换成string,方便比较和在map中存储。
string转换成char,方便进行变换。
第一次到的时候,顺次输出操作序列就是最小字典序的答案。可以用树的思想去考虑,每次按照ABC的顺序扩展一层,那么第一次到达的时候,一定是字典序最小的答案。
//woc看了y总的思路与代码实现,虽然比较繁琐
//但是一步一步来,是真的清晰!
//每次都按照ABC的顺序去搜,搜出来的就是字典序
#include<iostream>
#include<cstring>
#include<algorithm>
#include<unordered_map>
#include<queue>
using namespace std;
char g[2][4];
unordered_map<string, pair<char, string> > pre; //某个状态的前一个状态及对应的转移操作
unordered_map<string, int> dist;//每个状态和对应的步数
//string变换成矩阵
void set(string state)
{
for(int i=0; i<4; i++) g[0][i]=state[i];
for(int i=7, j=0; j<4; i--, j++) g[1][j]=state[i];
}
//矩阵变成string
string get()
{
string res;
for(int i=0; i<4; i++) res+=g[0][i];
for(int i=3; i>=0; i--) res+=g[1][i];
return res;
}
// 交换上下两行
string move0(string state)
{
set(state);
for(int i=0; i<4; i++) swap(g[0][i], g[1][i]);
return get();
}
// 将最右边的一列插入到最左边;
string move1(string state)
{
set(state);
int v0=g[0][3], v1=g[1][3];
for(int i=3; i>=1; i--)
{
g[0][i]=g[0][i-1];
g[1][i]=g[1][i-1];
}
g[0][0]=v0, g[1][0]=v1;
return get();
}
// 魔板中央对的4个数作顺时针旋转。
string move2(string state)
{
set(state);
int v=g[0][1];
g[0][1]=g[1][1];
g[1][1]=g[1][2];
g[1][2]=g[0][2];
g[0][2]=v;
return get();
}
int bfs(string start, string end)
{
if(start == end) return 0;
// 队列中需要存string
queue<string> q;
q.push(start);
dist[start]=0;
while(q.size())
{
auto t=q.front();
q.pop();
string m[3];//记录三个转移状态
m[0]=move0(t);
m[1]=move1(t);
m[2]=move2(t);
// 对于三个可能的转移状态
for(int i=0; i<3; i++)
if(!dist.count(m[i]))//寻找没有被找过的状态
{
dist[m[i]]=dist[t]+1;
// 存储{对应的操作,前一个状态}
pre[m[i]]={'A'+i, t};
// 入队
q.push(m[i]);
if(m[i]==end) return dist[end];
}
}
}
int main()
{
int x;
string start, end;
for(int i=0; i<8; i++)
{
cin>>x;
end+=char(x+'0');
}
// start是初始状态的字符串
for(int i=1; i<=8; i++) start+=char('0'+i);
int step=bfs(start, end);
cout<<step<<endl;
//记录操作
string res;
// 从后向前
while(end != start)
{
// res记录操作序列
res+=pre[end].first;
// end记录转移来的状态
end=pre[end].second;
}
// 前后反转一下。
reverse(res.begin(), res.end());
if(step>0) cout<<res<<endl;
return 0;
}