本题可以采用双向BFS,别名大模拟。
原因是本题的反向搜索方式与正向相同,也就是说存在反向搜索 的方式。
cqbzljsqwq
大巨佬的双向BFS强悍的八维数组状态本蒟蒻学不来QwQ
蒟蒻的当前状态就是一个六位数,直接扔到下标里面标记。
那么很明显,用六位数显然比用字符串做状态快了无数倍,同时也会麻烦无数倍。
比如,本人程序一直很慢的原因是:有超过两小时的时间一直认为存六位数的下标应该开1e7
的数组,($10^7-999999=9000001$)导致memset
直接爆炸。
设计状态:
struct node{
int now;//当前的六位数密码
int stp;//当前所用步数
int pos;//当前光标所在位置
bool flag;//搜索方向,0表示正向,1表示反向
}
left
和right
操作好说,直接改变pos
的值就行了。
但up/down,swap0/swap1
是要改变now
的某几位上的值,很麻烦。
我们先存储一个数从左向右每一位对应的单位:
const int ws[]={1000000,100000,10000,1000,100,10,1};
//防RE,第0位也要存
假设我们要改变num
在pos
上的值为x
,怎么操作呢?
我们将num
拆成1~(pos-1)
,pos
和(pos+1)~6
三部分。
第一部分和第三部分不变,将第二部分修改为x*ws[pos]
就行了。
1~(pos-1)
的获取方式:利用C++整除的特性,num/ws[pos-1]*ws[pos-1]
就可以得到了。
(pos+1)~6
的获取方式:num%ws[pos]
就可以得到pos
之后位数的值。
由此得出上下操作的代码:
//up or down,获取num的pos位增加chg的结果
inline int UpOrDown(int num,int pos,int chg){
int t=num/ws[pos]%10+chg;
//获取num的pos位变化后的结果,判断是否可以这样变化
if(t>=0&&t<=9)
return num/ws[pos-1]*ws[pos-1]+t*ws[pos]+num%ws[pos];
return num;
}
以及swap
操作:
//swap0 or swap1,获取num的pos位与chg位交换的结果
inline int Swap(int num,int pos,int chg){
int bg=num/ws[pos]%10;//记录pos位本来的数值
int ed=num/ws[chg]%10;//记录chg位本来的数值
if(bg==ed)return num;
num=num/ws[pos-1]*ws[pos-1]+ed*ws[pos]+num%ws[pos];//pos变为ed
num=num/ws[chg-1]*ws[chg-1]+bg*ws[chg]+num%ws[chg];//chg变为bg
return num;
}
然后是调到吐的双向BFS。
为了方便同时标记走没走过与所用步数,我们事先将vis
数组置为-1
。
完整代码:
/*
(twbfs==大模拟)==true
*/
#include<queue>
#include<cstdio>
#include<cstring>
using namespace std;
short vis[1000000][7][2];
struct node{
int now;
int stp,pos;
//pos: 当前光标位置
bool flag;
}S,B,f,tmp;
queue<node>q;
const int ws[]={1000000,100000,10000,1000,100,10,1};
inline int UpOrDown(int num,int pos,int chg){
int t=num/ws[pos]%10+chg;
if(t>=0&&t<=9)
return num/ws[pos-1]*ws[pos-1]+t*ws[pos]+num%ws[pos];
return num;
}
inline int Swap(int num,int pos,int chg){
int bg=num/ws[pos]%10;
int ed=num/ws[chg]%10;
if(bg==ed)return num;
num=num/ws[pos-1]*ws[pos-1]+ed*ws[pos]+num%ws[pos];
num=num/ws[chg-1]*ws[chg-1]+bg*ws[chg]+num%ws[chg];
return num;
}
int TWBFS(void){
//TIANWEN BFS(bushi)
while(!q.empty()){
tmp=f=q.front();
tmp.stp++;
q.pop();
if(~vis[f.now][f.pos][!f.flag])
return f.stp+vis[f.now][f.pos][!f.flag];
//将光标左移一位
if(f.pos>1){
tmp.pos=f.pos-1;
if(vis[tmp.now][tmp.pos][tmp.flag]==-1){
vis[tmp.now][tmp.pos][tmp.flag]=tmp.stp;
q.push(tmp);
}
}
//将光标右移一位
if(f.pos<6){
tmp.pos=f.pos+1;
if(vis[tmp.now][tmp.pos][tmp.flag]==-1){
vis[tmp.now][tmp.pos][tmp.flag]=tmp.stp;
q.push(tmp);
}
}
//记得把光标移回来,不要像本蒟蒻一样傻傻踩坑
tmp.pos=f.pos;
//将光标所在位置的数值增加1
tmp.now=UpOrDown(f.now,f.pos,1);
if(vis[tmp.now][tmp.pos][tmp.flag]==-1){
vis[tmp.now][tmp.pos][tmp.flag]=tmp.stp;
q.push(tmp);
}
//将光标所在位置的数值减少1
tmp.now=UpOrDown(f.now,f.pos,-1);
if(vis[tmp.now][tmp.pos][tmp.flag]==-1){
vis[tmp.now][tmp.pos][tmp.flag]=tmp.stp;
q.push(tmp);
}
//swap0
if(f.pos!=1){
tmp.now=Swap(f.now,f.pos,1);
if(vis[tmp.now][tmp.pos][tmp.flag]==-1){
vis[tmp.now][tmp.pos][tmp.flag]=tmp.stp;
q.push(tmp);
}
}
//swap1
if(f.now!=6){
tmp.now=Swap(f.now,f.pos,6);
if(vis[tmp.now][tmp.pos][tmp.flag]==-1){
vis[tmp.now][tmp.pos][tmp.flag]=tmp.stp;
q.push(tmp);
}
}
}
return 114514;
}
int main(){
scanf("%d%d",&S.now,&B.now);
memset(vis,-1,sizeof(vis));
S.pos=1;
q.push(S);
B.flag=1;
vis[S.now][1][0]=0;
//最终密码的光标在每一位都有可能出现
for(int i=1;i<=6;++i){
B.pos=i;
vis[B.now][i][1]=0;
q.push(B);
}
printf("%d\n",TWBFS());
return 0;
}
end.
这个题目放「简单」的原因是啥根据!?
可能对于NOI的大佬们,这个很简单了