传送门:P5507
对于大佬们来说,这题是裸的双向bfs题,但对于本蒟蒻来说,这是一道裸的 WA自动机(手动滑稽 )
当然,本蒟蒻也会看题解的,知道这是双向BFS之后,就决定尝试上代码,结果:双向BFS双倍爽
我是不会告诉你们我调代码调了一晚上的
先上AC代码:
#include<bits/stdc++.h>
using namespace std;
const int MAXN=(1<<25);
int f1[20],f2[20],f3[20],f4[20];
int vis[MAXN],fa[MAXN],fw[MAXN];
int choice[MAXN];
int ans1[MAXN],ans2[MAXN];
int state;
int m1,m2,mid;
bool flag=0;
int findstate(int t,int len)
{return (t>>((len-1)<<1))&3;}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
for(int i=1;i<=12;i++)
{
int now;
cin>>now>>f1[i]>>f2[i]>>f3[i]>>f4[i];
now--;
state=state^(now<<((i-1)<<1));
}
// cout<<(bitset<24>)state<<endl;
queue<int >q;
q.push(state);//初始状态压入
q.push(0);//最终态压入
fw[state]=1;fw[0]=-1;//1 表示正向 -1表示逆向
vis[state]=1;vis[0]=1;// 标记是否走过
while(!q.empty()&&flag==0)
{
int t=q.front();q.pop();// t 表示队首
for(int i=1;i<=12;i++)// 枚举所有可能发生变化的钮扣
{
int nnstate,nstate,nextstate;
int gai;// gai表示会发生连锁的纽扣号码
nstate=findstate(t,i);// findstate 用于找第 i 个纽的状态
//cout<<(bitset<24>)t<<endl;
//cout<<(bitset<2>)nstate<<endl;
if(fw[t]==1)// 如果是正向
{
if(nstate==0)gai=f1[i];
else if(nstate==1)gai=f2[i];
else if(nstate==2)gai=f3[i];
else if(nstate==3)gai=f4[i];
// cout<<(bitset<24>)t<<endl;
// cout<<(bitset<2>)nstate<<endl;
// cout<<(bitset<24>)(((nstate+1)&3)<<((i-1)<<1))<<endl;
nextstate=(t^(nstate<<((i-1)<<1)))^(((nstate+1)&3)<<((i-1)<<1));//首先转动第 i 个纽扣 nextstate表示转动后的状态 这里注意(nstate+1)&3要用括号包住
// cout<<i<<' '<<(bitset<24>)nextstate<<endl;
nnstate=findstate(nextstate,gai);
// cout<<gai<<' '<<(bitset<2>)nnstate<<endl;
// cout<<(bitset<24>)(nextstate^(nnstate<<((gai-1)<<1)))<<endl;
nextstate=nextstate^(nnstate<<((gai-1)<<1))^(((nnstate+1)&3)<<((gai-1)<<1));// 现在的next表示发生连锁之后的状态
// cout<<(bitset<24>)nextstate<<endl;
}
else//如果当前状态是反向的,就要回溯到前一步
{
nextstate=t^(nstate<<((i-1)<<1))^(((nstate+3)&3)<<((i-1)<<1));//nextstate 表示转动到t之前的状态
nstate=findstate(nextstate,i);
if(nstate==0)gai=f1[i];
else if(nstate==1)gai=f2[i];
else if(nstate==2)gai=f3[i];
else if(nstate==3)gai=f4[i];
nnstate=findstate(nextstate,gai);
nextstate=nextstate^(nnstate<<((gai-1)<<1))^(((nnstate+3)&3)<<((gai-1)<<1));
}
// cout<<(bitset<24>)nextstate<<endl;
if(vis[nextstate]==1)
{
if(fw[nextstate]==fw[t])continue;
else
{
m1=fw[t]==1?t:nextstate;
mid=i;
m2=fw[t]==1?nextstate:t;
flag=1;break;
}
}
vis[nextstate] = 1;
fw[nextstate] = fw[t]; //继承方向
fa[nextstate] = t; //用于回溯操作
choice[nextstate] = i ; //记录本次操作
q.push(nextstate);
}
}
//cout<<(bitset<24>)m1<<endl;
//cout<<(bitset<24>)m2<<endl;
int cnt1 = 0,st = m1,cnt2 = 0;
while(st!= state){
ans1[++cnt1] = choice[st];
st = fa[st];
}
//逆向回溯
st= m2;
while(st != 0)
{
ans2[++cnt2] = choice[st];
st= fa[st];
}
cout<<cnt1+cnt2+1<<endl;
for(int i = cnt1; i; i--) cout<<ans1[i]<<' ';
cout<<mid<<' ';
for(int i=1;i<=cnt2;i++) cout<<ans2[i]<<' ';
return 0;
}
这题的变量可以说多到令人疯狂,所以在写代码的时候还要一边给自己加注释。注释的部分是调代码的过程,还有好多删掉了,代码里保留的是最后一次调试的过程。
先聊聊双向BFS的思路吧。
这道题如果暴力来解就是BFS,把每一种可能的情况压入队列中,当出现终态的时候输出就行,但是明眼人都看的出来,暴力出不了奇迹,时间复杂度直接爆炸。
那么思路是BFS,怎么加速呢?一个不行那就来两个,双向BFS永远的神!
双向BFS适用于已知初态和终态的情况,从两段出发,相遇时就是答案。
思路极其简单,代码写的人飘起来。
再补充一个知识点
将数字以二进制的形式输出的方式为:cout<<(bitset<24>)t<<endl;
中间的24是输出24位,可以自由选择输出多少位。
这个方式在位运算参与的算法中可以起调试作用。