模板:这些的题型的大部分模板无非是在一个点有多个选择,直到找到最终答案,而困难一些的选项可能就是加一个路径
(题目截图均来自于Acwing)
经典例题:
魔板:
思路:这道题发现每一种变换形式都有3种变换选项,所以呢,这里可以通过将字符数组转换成字符串的形式改变排列方式,随后通过到达这一个排列方式的步数来判段这个方式是否走过,如果没走过加入队列中,且将这个点的前置点设为当前排列方式,并且需要记录怎么走的
这里代码有点长,但是3个操作无非是题中所给,都比较简单,set和get方法是字符串和字符数组相互转换的方式
实现代码:
#include<bits/stdc++.h>
using namespace std;
char g[2][4];
unordered_map<string,pair<char,string>> pre;//到这一步的前置是经过了哪步,且通过什么操作
unordered_map<string,int> dist;//到这一步经过了几次变换
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 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;
}
//3中move均为题中所给操作,如题意直接写
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>=0;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();
}
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;
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];
}
}
}
return -1;
}
int main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int x;
string start,end;
for(int i=0;i<8;i++){
cin>>x;
end+=char(x+'0');
}//从后往前才可以记录pre
for(int i=1;i<=8;i++) start+=char(i+'0');
int step=bfs(start,end);
cout<<step<<endl;
string res;
while(end!=start){
res+=pre[end].first;//记录前置点是哪个,怎么走的
end=pre[end].second;//走到他记录的前置点
}
reverse(res.begin(),res.end());//因为是从后往前的,所以翻转一下
if(step>0) cout<<res;
return 0;
}