POJ-1324:Holedox Moving(BFS+状态压缩+蛇蛇历险记)


题目链接:点击打开链接


题目大意:

有一条蛇,问他到终点的最短步数是多少。


解题思路:

刚开始看图不大打算写暴力不存状态裸搜过去的,然后就理所当然的 t 掉了。尼玛,这蛇的状态怎么存,后来看了一下别人的思路,大意就是用蛇的每两部分身体相连的方向做状压,每一部分相对于他后一部分的方向只有4种,在二进制中是两位,所以最多大概不到10w 的数就可以解决。


保存蛇的状态后注意还要保存头结点所在的位置,因为每一个头结点所在的为值和一个蛇的形状表示一个状态,所以可以用一个三维数组保存。剩下的就是正常的bfs过程,但是有一部分要注意,就是蛇自身的身体也是不能走的,即蛇不能自己咬自己,我本来的做法是在结构体中保存数组来记录蛇的身体当前所在的位置,然后每次有个数组每次都初始化来记录,但是 t 掉了。后来看了大佬的博客,蒙蔽了,太强了,大佬是只用头结点,然后利用二进制存的状态表示的方向。从而一步步逆推出蛇的身体。不禁折服,膜拜大佬。

这道题还可以用A*优化,暂时没啥时间,有时间再写,不优化大概2s多,优化后大概 0.2s多就可以。细节看代码,关于状压还是要好好理解位运算,理解了位运算状压就简单了,


#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
#include <algorithm>
#include <set>
#include <functional>
#define rank ra
#define lson rt<<1
#define rson rt<<1|1
#define pb push_back
#define hash haha
using namespace std;
typedef long long ll;
int n,m,l,sk,ans,scp;
int dx[4]={1,0,-1,0};
int dy[4]={0,-1,0,1};
bool vis[23][23][80000];    //三维存蛇状态
bool ma[25][25];
int x[10],y[10];
struct node
{
    int dog;
    int x,y;
    int step;
}st;
int vs(int x1,int y1,int x2,int y2)     //计算蛇的状态值
{
    for(int i=0;i<4;i++)
    {
        int xx=x2+dx[i];
        int yy=y2+dy[i];
        if(xx==x1&&yy==y1)      //注意方向 逆推要用到
        {
            if(i==0)
                return 0;
            if(i==1)
                return 1;
            if(i==2)
                return 2;
            return 3;
        }
    }
}
bool check(int x1,int y1,int x2,int y2,int s)   //逆推 判断是否合理
{
    int dir;
    for(int i=0;i<l-1;i++)
    {
        dir=3;
        dir=dir&s;
        s>>=2;
        if(x1==x2-dx[dir]&&y1==y2-dy[dir])
            return false;
        x2-=dx[dir];        //不断逆推至身体前一部分
        y2-=dy[dir];
    }
    return true;
}
void bfs()
{
    memset(vis,0,sizeof(vis));
    queue<node> que;
    st.step=0;
    que.push(st);
    vis[st.x][st.y][st.dog]=1;
    ans=-1;
    while(!que.empty())
    {
        node k=que.front();
        //printf("--%d %d\n",k.x,k.y);
        que.pop();
        if(k.x==1&&k.y==1)      //到达终点 结束
        {
            ans=k.step;
            break;
        }
        for(int i=0;i<4;i++)
        {
            int xx=k.x+dx[i];
            int yy=k.y+dy[i];
            int res=((k.dog&scp)<<2)|vs(xx,yy,k.x,k.y);     //通过当前状态直接得到下一阶段的状态  位运算!!
            if(vis[xx][yy][res]==0&&xx>=1&&xx<=n&&yy>=1&&yy<=m&&ma[xx][yy]==0&&check(xx,yy,k.x,k.y,k.dog))
            {
                node k1;
                k1.x=xx;k1.y=yy;
                k1.dog=res;
                k1.step=k.step+1;
                que.push(k1);
                vis[xx][yy][res]=1;
            }
        }
    }
}
int main()
{
    int kase=0;
    while(scanf("%d%d%d",&n,&m,&l)!=EOF)
    {
        if(n==0&&m==0&&l==0)
            break;
        int mm;
        int g=(l-2)*2;
        scp=(1<<g)-1;       //状压需要用到的中间变量 自行理解
        memset(ma,0,sizeof(ma));
        for(int i=1;i<=l;i++)
            scanf("%d%d",&x[i],&y[i]);
        sk=0;
        for(int i=l;i>1;i--)     //计算初始状态的值
            sk=(sk<<2)|vs(x[i-1],y[i-1],x[i],y[i]);
        st.dog=sk;
        st.x=x[1];st.y=y[1];
        scanf("%d",&mm);
        for(int i=1;i<=mm;i++)      //障碍所在位置
        {
            int p,q;
            scanf("%d%d",&p,&q);
            ma[p][q]=1;
        }
        bfs();
        printf("Case %d: %d\n",++kase,ans);
    }
    return 0;
}







  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值