poj3131 立体八数码,双向广搜

神题。

题目分析

状态巨多无比。
咱们知道应对这种神级题目有两种办法:哈希和双向广搜。那么在这道题的情况下,都要用。
哈希当然是链式哈希了,一开始搞了半天每弄明白,后来参考了一位大大的代码,就两边的广搜写在同一个哈希表里,不过作不同的标记,这样可以让代码变短
这位大大:http://blog.csdn.net/auto_ac/article/details/8790113
哈希就是用一个七进制数表示状态啦。状态按照顶面,前面,侧面的顺序:

0123456
顶面EWWBBRR
前面-RBRWBW
侧面-BRWRWB

下面就是双向广搜了,同样这位大大让我明白了一种双向广搜的办法:规定合适的层数,然后每次将这一层的状态都进行扩展。
然后因为只规定了目标状态的顶面,所以可以先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;
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值