洛谷P5507 机关(A*)

不会写双向BFS的,死也不会写的

考虑IDA*。一个很naive的h函数是:每个按钮到目标的距离的最大值。于是你花20min就可以拿到40分:

#include<cstdio>
short s[15],a[15][5],maxd=0,opt[20];
inline bool check(){
    for(short i=1;i<=12;++i)if(s[i]!=1)return 0;
    return 1;
}
inline short h(){
    short i,maxn=0;
    for(i=1;i<=12;++i)if(s[i]!=1&&5-s[i]>maxn)maxn=5-s[i];
    return maxn;
}
bool dfs(int d){
    if(d+h()>maxd)return 0;
    if(check())return 1;
    short i,t1,t2,p;
    for(i=1;i<=12;++i){
        t1=s[i];p=a[i][s[i]];t2=s[p];
        s[i]=(s[i]&3)+1;s[p]=(s[p]&3)+1;
        opt[d]=i;
        if(dfs(d+1))return 1;
        s[p]=t2;s[i]=t1;  //倒过来主要是为了p==i的情况
    }
    return 0;
}
int main(){
    short i,j;
    for(i=1;i<=12;++i){
        scanf("%d",s+i);
        for(j=1;j<=4;++j)scanf("%d",&a[i][j]);
    }
    for(maxd=0;maxd<18;++maxd)if(dfs(0))break;
    printf("%d\n",maxd);
    for(i=0;i<maxd;++i)printf("%d ",opt[i]);
    return 0;
}
View Code

然而,我们发现这个h函数相当不精确,因为每次最多修改两个按钮,稍加思考可以得到一个更精确的h:每个按钮到目标的距离的和的一半。事实证明这个下界相当紧,把上面代码中的h()换成如下代码即可获得80分:

inline short h(){
    short i,sum=0;
    for(i=1;i<=12;++i)if(s[i]!=1)sum+=5-s[i];
    return sum>>1;
}
View Code

那么剩下20分怎么拿呢?BFS:真香!没关系,IDA*被卡了,我们还有A*!难写多了

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
int pre[1<<24];
short a[15][5],d[1<<24],opt[1<<24],st[20],sl=-1;
struct node{
    int x;
    short g;  //g内保存的是A*中的f()与h()的和
    node(){}
    node(int x):x(x){
        g=0;
        for(short i=0;i<24;i+=2)if(x&(3<<i))g+=4-((x>>i)&3);
        g=(g>>1)+d[x];
    }
    inline bool operator <(const node &b)const{return g>b.g;}
};
priority_queue<node> Q;
inline short nxt(short x){return (x+1)&3;}
int main(){
    short i,j,s,t1,t2,p;
    int h,t=0;
    bool ok=0;
    for(i=0;i<12;++i){
        scanf("%d",&s);
        t|=s-1<<(i<<1);
        for(j=0;j<4;++j){scanf("%d",&a[i][j]);--a[i][j];}
    }
    memset(d,0x3f,sizeof(d));d[t]=0;
    Q.push(node(t));
    while(!Q.empty()&&!ok){
        h=Q.top().x;Q.pop();
        for(i=0;i<12;++i){
            t1=(h>>(i<<1))&3;p=a[i][t1];t=h^(t1<<(i<<1))^(nxt(t1)<<(i<<1));
            t2=(h>>(p<<1))&3;t^=(t2<<(p<<1))^(nxt(t2)<<(p<<1));
            //解码+产生新状态
            if(d[t]==0x3f3f){  //A*保证第一次碰到就是最优解
                d[t]=d[h]+1;
                pre[t]=h;
                opt[t]=i+1;
                if(!t){ok=1;break;}
                Q.push(node(t));
            }
        }
    }
    printf("%d\n",d[0]);
    while(pre[t]){
        st[++sl]=opt[t];
        t=pre[t];
    }
    while(sl>=0)printf("%d ",st[sl--]);
    return 0;
}
View Code

A*好像吊打BFS

posted @ 2019-08-14 15:23  wangyuchen  阅读(409)  评论(0编辑  收藏  举报