知识点: BFS+字符串hash+模拟
笔记:
第一次发题解,可能比较菜,请体谅QAQ,如果有错的话欢迎指出。
6个变换态分别写在bfs中,用广度优先搜索从第一个光标开始搜起,用map标记多少步数
到达该字符串,找到更新最大值即可,用了这思路,超时超的妈都不认识。(结果正确,18分)
所以这题果断舍弃map(看来以后要少用,两次教训),用一个6维数组标记密码(这里引入一个函数stoi,将字符串转变成数字,头文件为cstring),以空间换时间(六位不会炸),另外写一个函数给六维数组赋个值,然后的话另外弄一个二维数组,第一个空放6维数组,第二个空放下标,以此来标记状态,但依然过不了QAQ
然后的话,改成双向BFS,可以是可以但是依然是有区别的,由于最终状态的游标可以在任意地点,所以的话要把结尾的六个状态塞进去(游标分别在六个地方)。
但依旧TLE,看来只能手算了,把中间那部分自行手算解决。(再省去那部分循环的时间)同时中间可以加个优化,如果当前的数小于或大于目标数中的最小值或最大值,则禁止增加到比其大或比其小。(加上这个优化后不再TLE了,成绩涨到了55分QAQ)
然后找到了几个小细节就AC了。(在程序中标记出来)
AC代码解析如下:
#include<iostream>
#include<map>
#include<cstring>
#include<queue>
#include<algorithm>
using namespace std;
string s;
int best=0,now_cur=0,big=0;
struct pel{
string st;(这里放当前的字符串)
int dir;(这里标记是从起始状态还是末尾状态搜起)
int mark;(这个标记游标所在的位置)
};
bool sus=0;(判断是否已经找到答案)
queue<pel> q;(搜索队列)
string cur,edi;(开始和末尾的字符串)
int maxn=0;
int f[3000000][6],mid[3000000][6],pwd[10][10][10][10][10][10];(f用来记录步数,mid用来判断当前状态是起始的还是最终开始搜起的,而pwd则是用来给当前字符串哈希的)
int get_val(string tems){(用来得到它的哈希值)
int num[7];
num[1]=tems[0]-48;
num[2]=tems[1]-48;
num[3]=tems[2]-48;
num[4]=tems[3]-48;
num[5]=tems[4]-48;
num[6]=tems[5]-48;
return pwd[num[1]][num[2]][num[3]][num[4]][num[5]][num[6]];
}
void gi_val(string tems){
int num[7];
num[1]=tems[0]-48;
num[2]=tems[1]-48;
num[3]=tems[2]-48;
num[4]=tems[3]-48;
num[5]=tems[4]-48;
num[6]=tems[5]-48;
big++;(用big给予不同的数来哈希)
pwd[num[1]][num[2]][num[3]][num[4]][num[5]][num[6]]=big;
}
string swap0(string tems,int temm){(根据题意)
if(temm==0)return tems;
swap(tems[temm],tems[0]);
return tems;
}
string swap1(string tems,int temm){ (根据题意)
if(temm==5)return tems;
swap(tems[temm],tems[5]);
return tems;
}
string up(string tems,int temm){
if(tems[temm]>=maxn+48)return tems;(注意,这里剪枝只能是最大值,不然会WA)
tems[temm]=char(int(tems[temm])+1);
return tems;
}
string down(string tems,int temm){
if(tems[temm]=='0')return tems;
tems[temm]=char(int(tems[temm])-1);
return tems;
}
void check() { (判断最糟糕的情况的best,并以此来剪枝)
int a=cur[0]-48,b=cur[1]-48,c=cur[2]-48,d=cur[3]-48,
e=cur[4]-48,f=cur[5]-48;
int A=edi[0]-48,B=edi[1]-48,C=edi[2]-48,D=edi[3]-48,
E=edi[4]-48,F=edi[5]-48;
best=abs(a-A)+abs(b-B)+abs(c-C)+abs(d-D)+abs(e-E)+abs(f-F)+5;
}
void imp(string nes,int nemk,string nws,int nwmk,int direc){
if(nes==nws)return ;
pel temp;
temp.st=nes,temp.mark=nemk,temp.dir=direc;
if(get_val(nes)==0)gi_val(nes);(如果这个字符串没有被哈希过给他个哈希值)
int nex_mar=get_val(nes);(得到哈希值方便当前操作)
if(direc==mid[nex_mar][nemk])return ;
if((direc==1&&mid[nex_mar][nemk]==3)||(direc==3&&mid[nex_mar][nemk]==1)){(当撞头了肯定是最小的距离,直接标记sus=1(因为bfs是逐步延伸扩展开的,先碰到的肯定是最小的))
best=f[nex_mar][nemk]+f[now_cur][nwmk];
sus=1;
return ;
}
if(!f[nex_mar][nemk]||f[nex_mar][nemk]>f[now_cur][nwmk]+1){
f[nex_mar][nemk]=f[now_cur][nwmk]+1;
mid[nex_mar][nemk]=direc;(延续前面的路线)
q.push(temp);
}
}
int bfs(){
pel next;int i;
next.st=cur,next.mark=0;next.dir=1;
q.push(next),gi_val(cur);
f[1][0]=0,mid[1][0]=1;
next.st=edi,gi_val(edi),next.dir=3;
for(i=0;i<=5;i++){(由于有6个游标,所以都需要塞进去)
next.mark=i;
f[2][i]=1;(这里必须是1,因为当我们正向搜过去的时候其倒数的步数会加1到最终状态,而双向的要考虑到这一点)
mid[2][i]=3;
q.push(next);
}(前面这些都是双向BFS的准备工作)
int now_mk=0,now_dir;
string now_st;
while(!q.empty()){
now_st=q.front().st;
now_mk=q.front().mark;
now_cur=get_val(now_st);
now_dir=q.front().dir;
q.pop();
if(f[now_cur][now_mk]>best/2)continue;(其中一个剪枝)
剩下的都是大模拟
next.st=swap0(now_st,now_mk);
next.mark=now_mk;
imp(next.st,next.mark,now_st,now_mk,now_dir);
if(sus)return best;
next.st=swap1(now_st,now_mk);
next.mark=now_mk;
imp(next.st,next.mark,now_st,now_mk,now_dir);
if(sus)return best;
next.st=up(now_st,now_mk);
next.mark=now_mk;
imp(next.st,next.mark,now_st,now_mk,now_dir);
if(sus)return best;
next.st=down(now_st,now_mk);
next.mark=now_mk;
imp(next.st,next.mark,now_st,now_mk,now_dir);
if(sus)return best;
next.st=now_st;
next.dir=now_dir;
if(now_mk>=1){
next.mark=now_mk-1;
if(!f[now_cur][next.mark]||f[now_cur][next.mark]>f[now_cur][now_mk]+1){
f[now_cur][next.mark]=f[now_cur][now_mk]+1;
mid[now_cur][next.mark]=now_dir;
q.push(next);
}
}
if(now_mk<=4){
next.mark=now_mk+1;
if(!f[now_cur][next.mark]||f[now_cur][next.mark]>f[now_cur][now_mk]+1){
f[now_cur][next.mark]=f[now_cur][now_mk]+1;
mid[now_cur][next.mark]=now_dir;
q.push(next);
}
}
}
return best;
}
int main(){
memset(mid,2,sizeof(mid));
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);(输出优化)
cin>>cur;
cin>>edi;
check();
int i;
for(i=0;i<=5;i++)
maxn=max(maxn,edi[i]-48);
if(best==5){
cout<<0;
return 0;
}
cout<<bfs();
}
总结:这题咋一看题面好像很水,其实一堆细节的东西,并且还要加上优化不然铁定会TLE,还要注意MLE,粗略估计题目数据范围,事实证明,STL虽然好用但是在很多时候其会严重拖慢程序运行速度,使用需谨慎。