神题。
题目分析
状态巨多无比。
咱们知道应对这种神级题目有两种办法:哈希和双向广搜。那么在这道题的情况下,都要用。
哈希当然是链式哈希了,一开始搞了半天每弄明白,后来参考了一位大大的代码,就两边的广搜写在同一个哈希表里,不过作不同的标记,这样可以让代码变短。
这位大大:http://blog.csdn.net/auto_ac/article/details/8790113
哈希就是用一个七进制数表示状态啦。状态按照顶面,前面,侧面的顺序:
0 | 1 | 2 | 3 | 4 | 5 | 6 | |
---|---|---|---|---|---|---|---|
顶面 | E | W | W | B | B | R | R |
前面 | - | R | B | R | W | B | W |
侧面 | - | B | R | W | R | W | B |
下面就是双向广搜了,同样这位大大让我明白了一种双向广搜的办法:规定合适的层数,然后每次将这一层的状态都进行扩展。
然后因为只规定了目标状态的顶面,所以可以先dfs出256种目标状态都加入从后往前广搜的队列里。
具体还是看代码吧,
1485ms,但是只有130行。主要是因为代码码长了我自己不愿意调。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<climits>
#include<iomanip>
#include<algorithm>
#include<queue>
using namespace std;
//上前侧 0 E,1 WRB,2 WBR,3 BRW,4 BWR,5 RWB,6 RBW
const int maxn=1000000;
struct node{int num;int ee;int dep;}s,t;
//num:当前状态,ee:空格的位置,dep:搜索层数
queue<node>q1,q2;
char mp[9];
int h[maxn],tk[maxn],ne[maxn];
bool mak[maxn],vis[maxn];//vis:q1,mak:q2
int shi[9]={100000000,10000000,1000000,100000,10000,1000,100,10,1};
//shi:小小的预处理,具体看移动函数里这个的用法
int em,mod=999987,tot;
int sx[7]={0,5,4,6,2,1,3},zy[7]={0,3,6,1,5,4,2};//移动法则(自己推一推?)
void add(int now){//添加神奇的哈希
int kl=now%mod+1,i;
tot++;ne[tot]=h[kl];h[kl]=tot;
tk[tot]=now;
}
int find(int now){//寻找神奇的哈希
int kl=now%mod+1,i;
for(i=h[kl];i!=-1;i=ne[i])
if(tk[i]==now)return i;
add(now);//添加
return -1;
}
void dfs(int now,int x){//dfs出所有可行目标状态
if(x==9){
t.ee=em;t.num=now;t.dep=0;
q2.push(t);add(now);
mak[tot]=1;return;
}
if(mp[x]=='W')
dfs(now*10+1,x+1),dfs(now*10+2,x+1);
else if(mp[x]=='B')
dfs(now*10+3,x+1),dfs(now*10+4,x+1);
else if(mp[x]=='R')
dfs(now*10+5,x+1),dfs(now*10+6,x+1);
else if(mp[x]=='E')
dfs(now*10,x+1);
}
bool mv(node &v,int x){//移动
int kl=v.num,kll,tq;
if(x==0){//下
if(v.ee>=6)return 0;
kll=v.ee+3;tq=(kl/shi[kll])%10;
kl-=shi[kll]*tq;kl+=shi[v.ee]*sx[tq];
v.ee=kll;v.num=kl;
}
else if(x==1){//上
if(v.ee<=2)return 0;
kll=v.ee-3;tq=(kl/shi[kll])%10;
kl-=shi[kll]*tq;kl+=shi[v.ee]*sx[tq];
v.ee=kll;v.num=kl;
}
else if(x==2){//右
if(v.ee==2||v.ee==5||v.ee==8)return 0;
kll=v.ee+1;tq=(kl/shi[kll])%10;
kl-=shi[kll]*tq;kl+=shi[v.ee]*zy[tq];
v.ee=kll;v.num=kl;
}
else if(x==3){//左
if(v.ee==0||v.ee==3||v.ee==6)return 0;
kll=v.ee-1;tq=(kl/shi[kll])%10;
kl-=shi[kll]*tq;kl+=shi[v.ee]*zy[tq];
v.ee=kll;v.num=kl;
}
v.dep++;return 1;
}
void bfs(){//双向广搜
int i,j=0,k,mm;//i是q1的深度,j是q2的深度
for(i=0;i<=20;i++){//分配q1和q2的深度
node u;
while(!q1.empty()&&q1.front().dep==i){
u=q1.front();q1.pop();
for(k=0;k<4;k++){
node v=u;
if(!mv(v,k))continue;
mm=find(v.num);
if(mm==-1){q1.push(v);vis[tot]=1;}
else if(mak[mm]){printf("%d\n",i+j+1);return;}
}
}
while(!q2.empty()&&q2.front().dep==j&&j<9){
u=q2.front();q2.pop();
for(k=0;k<4;k++){
node v=u;
if(!mv(v,k))continue;
mm=find(v.num);
if(mm==-1){q2.push(v);mak[tot]=1;}
else if(vis[mm]){printf("%d\n",i+j+2);return;}
}
}
if(j<9)j++;
}
printf("-1\n");
}
int main()
{
int i,j,ex,ey;
while(1){
scanf("%d%d",&ey,&ex);
if(!ex&&!ey)break;ex--;ey--;
while(!q1.empty())q1.pop();
while(!q2.empty())q2.pop();
memset(h,-1,sizeof(h));
memset(mak,0,sizeof(mak));
memset(vis,0,sizeof(vis));
em=ex*3+ey;tot=0;
if(!em)s.num=0;else s.num=1;
s.ee=em;s.dep=0;s.num=111111111;
s.num-=shi[em];
q1.push(s);
for(i=0;i<9;i++){
mp[i]=getchar();
if(mp[i]==' '||mp[i]=='\n')i--;
if(mp[i]=='E')em=i;
}
dfs(0,0);
if(mak[find(s.num)]){printf("0\n");continue;}//特判终点即起点
bfs();
}
return 0;
}