P4289 [HAOI2008]移动玩具(bfs)

P4289 [HAOI2008]移动玩具

双向bfs+状态压缩+记忆化搜索

双向bfs用于对bfs的优化,每次找到可扩展节点少的一边进行一次bfs,找到的第一个互相接触的点即为最短路径

矩阵范围仅4*4大小,我们容易想到用二进制数压缩其状态,利于求解。

既然转成二进制,大小又<2^17,那么可以再加数组进行记忆化

不要忘了起点终点相同时的特判qwq

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
struct data{int zip,step;};
int ans,rec1[65538],rec2[65538]; //rec:记忆化用
queue <data> h[2]; //分别代表从起点/终点开始的bfs队列
inline void check(int to,int p,data x){ //检查该点是否符合条件
    if(rec1[to]!=-1){ 
        if(rec2[to]!=p) ans=rec1[to]+x.step+1; //如果该点已被对面搜到,那么已经得出最优解
    }else{
        rec1[to]=x.step+1,rec2[to]=p;
        h[p].push((data){to,x.step+1});
    }
}
void output(int t){ //检查用,将10进制数转回4*4的二进制矩阵
    cout<<"---
";
    int h[19];
    for(int k=t,i=15;i>=0;--i) h[i]=k&1,k>>=1;
    for(int i=0;i<4;++i){
            for(int j=i*4;j<=i*4+3;++j) cout<<h[j];
            cout<<endl;
        }
    cout<<"---
";
}
inline void bfs(){
    int p=(h[0].size()>h[1].size()),now=(h[p].front()).step; //找到可扩展节点少的一边,并且只扩展一层
    while(!h[p].empty()){
        data x=h[p].front();
        if(x.step!=now||ans) break;
        h[p].pop();
        int k=1,to;
        for(int i=1;i<65536;i<<=1,++k){ //用二进制数表示转移过程
            if(!(x.zip&i)) continue;
            if(k%4!=0&&(!(x.zip&(i<<1)))){ //向左
                to=x.zip-i+(i<<1);
                check(to,p,x);
            }
            if(k%4!=1&&(!(x.zip&(i>>1)))){ //向右
                to=x.zip-i+(i>>1);
                check(to,p,x);
            }
            if(k>4&&(!(x.zip&(i>>4)))){ //向下
                to=x.zip-i+(i>>4);
                check(to,p,x);
            }
            if(k<13&&(!(x.zip&(i<<4)))){ //向上
                to=x.zip-i+(i<<4);
                check(to,p,x);
            }
        }
    }
}
int main(){
    memset(rec1,-1,sizeof(rec1));
    char q[6]; int tot1=0,tot2=0;
    for(int i=1;i<=4;++i){  //矩阵转成十进制数
        cin>>q;
        for(int j=0;j<=3;++j) tot1+=q[j]-48,tot1<<=1; 
    }tot1>>=1; //注意最后要右移一位
    h[0].push((data){tot1,0}); rec1[tot1]=0; rec2[tot1]=0;
    for(int i=1;i<=4;++i){
        cin>>q;
        for(int j=0;j<=3;++j) tot2+=q[j]-48,tot2<<=1; 
    }tot2>>=1;
    h[1].push((data){tot2,0}); rec1[tot2]=0; rec2[tot2]=1;
    while(!ans&&tot1!=tot2) bfs(); //注意特判
    printf("%d",ans);
    return 0;
}